2013-03-11 6 views
6

Rozważmy następujące klasy:Jak spłaszczyć obiekt, do którego następuje odwołanie, do dwóch właściwości json.net w referer?

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

moim celem jest to, jak serializacji json używając newtonsofts json.net tak:

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

Dlaczego chcesz to zrobić? Ponieważ chcę używać Jsona jako moich DTO bez generowania pośredniej warstwy dynamicznych obiektów. Generowanie DTO powinno odbywać się dynamicznie według konwencji, a nie jawnie, imho. Wiem, że niektórzy mogą zdecydowanie nie zgodzić się z tym, ale omawianie mojego podejścia jest poza tym punktem. Chcę tylko wiedzieć, czy i jak można to zrobić.

Wyzwanie polega na tym, że użycie właściwości JsonPropertyAttribute dla właściwości Superior da tylko jedną właściwość jako wynik, w której potrzebuję dwóch. Jeśli użyję JsonObjectAttribute, otrzymam atrybut zagnieżdżony i będę miał problem z spłaszczeniem najwyższego poziomu użytkownika.

Na szczęście wygląda na to, że w bibliotece json.net jest wystarczająco dużo chronionych i/lub publicznych właściwości i metod, które mogę rozszerzyć, aby uzyskać pożądany rezultat. Pozostaje zatem pytanie, z którymi klasami i metodami powinienem zacząć, aby dostać się tam, gdzie chcę iść? Czy wywodzi się z DefaultContractResolver, a nadpisywanie metody GetProperties to dobre miejsca, czy powinienem szukać gdzie indziej?

Odpowiedz

2

Krótka odpowiedź brzmi: tak, to odpowiednie miejsca do rozpoczęcia. Oto, co skończyło się (na razie):

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 jest tylko pusty atrybut, który używam do opisywania których odniesienie właściwości Chcę serializacji. Ważne jest to, że NIE przypisuję tego do niczego, co Json.NET rozpozna i użyje do wygenerowania JsonProperty. W ten sposób nie otrzymam duplikatu JsonProperty z mojego CreateProperties.

Alternatywnie mogłem wywodzić się z DataMemberAttribute i szukać, modyfikować i klonować JsonProperty, aby reprezentować moje Id i Name.

Dla mojego api web asp.net I ustawić ten MyContractResolver jako ContractResolver JsonFormatter.SerializerSettings.

Więc to naprawiło mnie do serializacji. Do deserializacji mam niestandardowy obiekt ChangeSet, w którym przechowuję PropertyInfo i obiekty. Podczas deserializacji upewniam się, że zachowam identyfikatory, a następnie usuwam te z mojego magazynu danych, w moim przypadku używając niestandardowego ActionFiltera z dostępem do sesji magazynu danych.

Oto esencja mojego serializacji:

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

A presto, wszystkie krwawe szczegóły moich czystych podmiotów i dynamicznego serializacji są kapsułkach, niestety w dwóch miejscach, ale to nie może być pomógł odkąd nie chcę uzyskać dostępu do mojego źródła danych z serializera.

Powiązane problemy