2013-03-11 5 views
6

Рассмотрим следующий класс:Как сгладить ссылочный объект на два свойства json.net в реферере?

public class User 
{ 
    public virtual int Id {get;set;} 
    public virtual string Name {get;set;} 
    public virtual User Superior {get;set;} 
} 

Моя цель заключается в сериализации это как JSON с помощью newtonsofts json.net так:

{ 
    Id: 101, 
    Name: 'Mithon', 
    SuperiorId: 100, 
    SuperiorName: 'TheMan' 
} 

Почему я хочу, чтобы это сделать? Потому что я хочу использовать Json как мой DTO без создания промежуточного слоя динамических объектов. Генерация DTO должна выполняться динамически по соглашению, а не явно, imho. Я знаю, что некоторые могут сильно не соглашаться с этим, но обсуждение моего подхода - это не главное. Я просто хочу знать, как и как это можно сделать.

Задача состоит в том, что использование свойства JsonPropertyAttribute для свойства Superior даст только одно свойство как выход, где мне нужно два. Если я использую JsonObjectAttribute, я получу вложенный атрибут, и у меня возникнут проблемы с тем, что пользователь верхнего уровня будет сплющен.

К счастью, в библиотеке json.net достаточно защищенных и/или общедоступных свойств и методов, что я могу расширить что-то, чтобы получить желаемый результат. Тогда возникает вопрос, с какими классами и методами я должен начать, где я хочу идти? Получается ли из DefaultContractResolver и переопределение метода GetProperties - это хорошие места или я должен искать в другом месте?

ответ

2

Короткий ответ: да, это были бы подходящие места для начала. Вот что я закончил с (на данный момент):

public class MyContractResolver : DefaultContractResolver 
{ 
    protected override IList<JsonProperty> CreateProperties(Type type, MemberSerialization memberSerialization) 
    { 
     var properties = base.CreateProperties(type, memberSerialization); 

     foreach (var pi in type.GetProperties().Where(pi => typeof (Entity).IsAssignableFrom(pi.DeclaringType) && Attribute.IsDefined((MemberInfo) pi, typeof(IdNameMemberAttribute)))) 
     { 
      properties.Add(CreateReferenceProperty(pi, Reflect.GetProperty<Entity>(e => e.Id))); 
      properties.Add(CreateReferenceProperty(pi, Reflect.GetProperty<Entity>(e => e.Name))); 
     } 

     return properties; 
    } 

    private JsonProperty CreateReferenceProperty(PropertyInfo reference, PropertyInfo referenceMember) 
    { 
     var jsonProperty = base.CreateProperty(reference, MemberSerialization.OptOut); 
     jsonProperty.PropertyName += referenceMember.Name; 
     jsonProperty.ValueProvider = new ReferencedValueProvider(reference, referenceMember); 
     jsonProperty.Writable = false; 

     return jsonProperty; 
    } 
} 

IdNameMemberAttribute просто пустой атрибут, который я использую, чтобы комментировать, какие свойства ссылки я хочу сериализации. Важным моментом является то, что я НЕ комментирую его ничем, что Json.NET будет распознавать и использовать для создания JsonProperty. Таким образом, у меня не получается дублировать JsonProperty из моего CreateProperties.

В качестве альтернативы я мог бы получить из DataMemberAttribute и искал, модифицировал и клонировал JsonProperty для представления моих Id и Name.

Для моего asp.net web api я установил этот MyContractResolver как ContractResolver из JsonFormatter.SerializerSettings.

Так что я исправил сериализацию. Для десериализации у меня есть собственный объект ChangeSet, где я храню PropertyInfo и объекты. Во время десериализации я затем удостоверяюсь, что сохраняю идентификаторы, а затем разрешаю их из своего хранилища данных, в моем случае используя пользовательский ActionFilter с доступом к сеансу хранилища данных.

Вот суть моего серийности:

var jsonSource = streamReader.ReadToEnd(); 
var deserializedObject = JsonConvert.DeserializeObject(jsonSource, type, SerializerSettings); 
var changeSet = deserializedObject as PropertyChangeSet; 
if (changeSet != null) 
{ 
    var jsonChange = JObject.Parse(jsonSource)["Change"].Cast<JProperty>().ToArray(); 

    IDictionary<string, int> references = jsonChange.Where(IsReferenceId).ToDictionary(t => t.Name.Substring(0, t.Name.Length - 2), t => t.Value.ToObject<int>()); 
    changeSet.References = references; 

    var properties = jsonChange.Where(jp => !IsReferenceId(jp)).Select(t => t.Name).ToList(); 
    changeSet.Primitives = properties; 
} 

И вуаля, все окровавленные детали моих чистых сущностей и динамической сериализации инкапсулированы, к сожалению, в двух местах, но это не может быть оказана помощь, так как я не хотите получать доступ к источнику данных из сериализатора.

Смежные вопросы