2015-11-30 5 views
1

В моем приложении есть класс CoupledType, который используется в более крупном объекте типа UserObject, где его экземпляры сохраняются путем сериализации с использованием Json.NET и хранятся в базе данных.Convert Class on Json Deserialization

Этот класс нуждается в существенном рефакторинге, чтобы он работал в большем количестве контекстов. Его имя также не идеально, и я бы хотел переместить его в другую библиотеку. Тем не менее, он все равно будет распространять один и тот же базовый класс и, самое главное, , когда пользователи десериализуют сохраненную работу, исходный экземпляр должен быть преобразован.

Класс в его текущем состоянии выглядит, примерно, так:

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 будет установлено на значение оригинала CoupledValue от .

Потенциально более сложная часть - это то, где мне нужен доступ к родительскому объекту этого объекта UserObject (то есть к его десериализации), чтобы получить ссылку на значение ContractType.


Подводя итог, нужно написать конвертер для Json.NET, который делает следующее:

  • Изменить имя типа объекта от FirstLibrary.Objects.CoupledType к SecondLibrary.Objects.DecoupledType
  • Заменить double CoupledValue с GenericContract Contract (конструкция которого требует доступа к ParentObject, этот экземпляр CoupledType является членом /, десериализуемым от.

У меня нет большого опыта конвертирования типов в Json (написав подкласс Json.NetJsonConverter, чтобы преобразовать число double объектов в double[] s. У меня нет опыта изменения имен типов или свойств. Если бы кто-нибудь мог хотя бы указать мне на потенциальное решение, это было бы очень признательно.


Образец Существующие (спекулятивный, так как на самом деле сериализовать в 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 
+0

Вы можете предоставить компактный и небольшой образец того, что вы хотите, и где он идет не так? –

+0

Обновленный вопрос, с надеждой, более четкое определение проблемы. Благодаря! –

+0

опубликуйте образец сериализованного json и десериализованную форму, которую вы хотите увидеть. –

ответ

1

Я не уверен если эти подходы не смогут удовлетворить какое-то невысказанное требование, но я могу придумать несколько потенциальных подходов, которые могли бы получить ваш DecoupledType f проложите провод в контроллер.

Вы можете создать обычай 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 (или объявить поставщик связующего явно по методу маршрута)

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; 
} 

Надеется, что это ставит вас на добиваться успеха.