簡體   English   中英

在Json反序列化上轉換類

[英]Convert Class on Json Deserialization

我的應用程序中有一個CoupledType類, CoupledType用於更大的UserObject類型的對象中,該對象的實例通過使用Json.NET進行序列化來保存並存儲在數據庫中。

此類需要大量重構以使其在更多上下文中工作。 它的名稱也不理想,我想將其移至另一個庫。 但是,它仍將擴展相同的基類,並且至關重要的是, 當用戶反序列化保存的工作時,必須轉換原始實例。

當前狀態的類大致如下所示:

namespace FirstLibrary.Objects
{
    [DataContract]
    public class CoupledType : BaseType
    {
        [DataMember]
        public double CoupledValue { get; set; } 
    }
}

我想將其轉換為以下類型:

namespace SecondLibrary.Objects
{
    [DataContract]
    public class DecoupledType : BaseType
    {
        [DataMember]
        GenericContract Contract { get; set; }
    }
}

上面提到的GenericContract類看起來像這樣:

[DataContract]
public class GenericContract
{
    [DataMember]
    public ContractType ContractType { get; set; }

    [DataMember]
    public double ContractValue { get; set; }
}

在轉換過程中,我想創建一個新的GenericContract對象,得到了ContractType從其他地方(其值將始終是相同的,在這個轉換)和ContractValue將被設置為原來的值CoupledValueCoupledType

可能更棘手的部分是我需要訪問該對象的父UserObject (即要從其反序列化的對象)以獲取對ContractType值的引用。


總而言之,我需要為Json.NET編寫一個轉換器,該轉換器執行以下操作:

  • 將對象的類型名稱從FirstLibrary.Objects.CoupledTypeSecondLibrary.Objects.DecoupledType
  • GenericContract Contract替換double CoupledValue (其構造需要訪問ParentObjectCoupledType實例是/反序列化的成員)。

我在Json中轉換類型沒有很多經驗(已經編寫了Json.NetJsonConverter的子類來將多個double對象轉換為double[] 。我沒有更改類型或屬性名稱的經驗。如果任何人至少可以指出我一個潛在的解決方案,將不勝感激。


現有樣本 (推測性的,在BSON中實際序列化)

{
    "$type": "FirstLibrary.Objects.CoupledType",
    "CoupledValue": 4
}

{
    "$type": "SecondLibrary.Objects.DecoupledType",
    "Contract": {
        "$type": "SecondLibrary.Objects.GenericContract",
        "ContractType": {/*Refers to an object serialized elsewhere*/},
        "ContractValue": 4
    }
}

如果有幫助,序列化設置如下:

ConstructorHandling = ConstructorHandling.AllowNonPublicDefaultConstructor,
TypeNameHandling = TypeNameHandling.All

我不確定這些方法是否不能滿足某些未說的要求,但是我可以想到一些可能的方法,這些方法可以將DecoupledType從電線連接到控制器中。

您可以創建一個自定義HttpParameterBinding ,然后在HttpParameterBinding執行映射。 輸入的參數信息存儲在HttpParameterDescriptor ,您可以查詢參數以確定特定綁定是否適用。

public class GenericContractBinding : HttpParameterBinding
{
   public GenericContractBinding(HttpParameterDescriptor descriptor) : base(descriptor){}
   public override Task ExecuteBindingAsync(ModelMetadataProvider provider, HttpActionContext context, CancellationToken cancellationToken)
   {
      if(context.ControllerContext.Configuration.DependencyResolver != null)
      {
         //This is a naive eval based only on the incoming type. You'll likely want to map
         var bazinga = context.Request.GetDependencyScope().GetService(Descriptor.ParameterType);
         if(bazinga.GetType() == typeof(GenericContract)
            context.ActionArguments[Descriptor.ParameterName] = bazinga;
      }
   }
}

另外,您還可以創建一個自定義Web API ModelBinder並用ModelBinderAttribute裝飾您的類型(或在route方法上顯式聲明綁定程序提供程序)

public class DecoupledTypeModelBinder : IModelBinder
{
   public bool BindModel(System.Web.Http.Controllers.HttpActionContext actionContext, ModelBindingContext bindingContext)
   {

      //I'm assuming your JSON is being sent in the content of the request
      string content = actionContext.Request.Content.ReadAsStringAsync().Result;
      var json = JArray.Parse(content); //you really should switch to "array by default" processing;the number of items in it is an implementation detail.

      //In my opinion, this "ContractTypeService" gets injected at CompositionRoot, which would be in the IHttpControllerActivator
      var contractType = actionContext.RequestContext.Configuration.DependencyResolver.GetService(typeof(ContractTypeService)).GetAmbientContractType();

      List<GenericContract> contracts = new List<GenericContract>();
      foreach(var item in json.Children<JObject>())
      {    
           var contract = new GenericContract();
           contract.ContractValue = (double)item["ContractValue"].Value<JToken>();
           contract.ContractType = contractType;
           contracts.Add(contract);
         }
      }

      //YMMV; You could enforce a hard requirement here for singularity or do something else if multiples are inbound on the wire
      DecoupledType model = new DecoupledType()
      {
         Contract = contracts.Single()
      };

      bindingContext.Model = model;
   }
}

public class DecoupledTypeModelBinderProvider : ModelBinderProvider
{
   public override IModelBinder GetBinder(System.Web.Http.HttpConfiguration configuration, Type modelType)
   {
      return new DecoupledTypeModelBinder();
   }
}
...(in your controller)
public dynamic Post([ModelBinder(typeof(DecoupledTypeModelBinderProvider))]DecoupledType bazinga)
{
    var contract = bazinga.Contract;
    var contractType = contract.ContractType;
    var contractValue = contract.ContractValue;
}

希望這能使您走上成功之路。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM