2016-05-01 2 views
9

я наткнулся на сервис, который выводит JSON в следующем формате:Json.NET Пользовательское JsonConverter с типами данных

{ 
    "Author": "me", 
    "Version": "1.0.0", 
    "data.Type1": { 
     "Children": [ 
      { 
       "data.Type1": { 
        "Children": [ 
         { 
          "data.Type2": { 
           "name": "John", 
           "surname": "Doe" 
          } 
         } 
        ] 
       } 
      }, 
      { 
       "data.Type3": { 
        "dob": "1990-01-01" 
       } 
      } 
     ] 
    } 
} 

имен типов данных, сохраняемые в качестве имен свойств и их значения являются реальными объектами. Все они начинаются с префикса data..

То, что я хотел бы получить потом что-то вроде этого:

{ // Root 
    "Author": "me", 
    "Version": "1.0.0", 
    "Children": [ // Type1 
     { 
      "Children": [ // Type1 
       { // Type2 
        "Name": "John", 
        "Surname": "Doe" 
       } 
      ] 
     }, 
     { // Type3 
      "DoB": "1990-01-01" 
     } 
    ] 
} 

со следующими классами:

class Type1 { 
    ICollection<object> Children { get; set; } 
} 

class Type2 { 
    public string Name { get; set; } 
    public string Surname { get; set; } 
} 

class Type3 { 
    public DateTime DoB { get; set; } 
} 

class Root 
{ 
    public string Author { get; set; } 
    public string Version { get; set; } 
    public Type1 Children { get; set; } 
} 

Вопрос

Как десериализации это в добавил C#, учитывая типы данных и удаляя их из дерева?

Я пробовал с пользовательским JsonConverter, но я борюсь с тем, как динамически выбирать конвертер, поскольку самым простым способом было бы разместить атрибут свойства, но он не поддерживается.

Небольшой пример будет замечательным.

ответ

8

Хотя этот формат JSON является несколько необычным и сопротивляется использование атрибутов из-за динамических имен свойств, это все еще возможно сделать JsonConverter, чтобы десериализовать его в вашу предпочитаемую структуру классов с одним небольшим изменением: я бы порекомендовал вам изменить свойство Children в классе , чтобы быть ICollection<object>, чтобы зеркально отобразить Children в Type1 класс. Как и сейчас, он не соответствует структуре вашего желаемого вывода (где Children показан как массив, а не объект), и в противном случае потребовался бы дополнительный код в конвертере для правильной обработки.

class Root 
{ 
    public string Author { get; set; } 
    public string Version { get; set; } 
    public ICollection<object> Children { get; set; } 
} 

Вот что я придумал для преобразователя (при условии, выше изменение сделано):

class CustomConverter : JsonConverter 
{ 
    public override bool CanConvert(Type objectType) 
    { 
     return (objectType == typeof(Root)); 
    } 

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) 
    { 
     JObject obj = JObject.Load(reader); 
     Root root = new Root(); 
     root.Author = (string)obj["Author"]; 
     root.Version = (string)obj["Version"]; 
     root.Children = ((Type1)DeserializeTypeX(obj, serializer)).Children; 
     return root; 
    } 

    private object DeserializeTypeX(JObject obj, JsonSerializer serializer) 
    { 
     JProperty prop = obj.Properties().Where(p => p.Name.StartsWith("data.")).First(); 
     JObject child = (JObject)prop.Value; 
     if (prop.Name == "data.Type1") 
     { 
      List<object> children = new List<object>(); 
      foreach (JObject jo in child["Children"].Children<JObject>()) 
      { 
       children.Add(DeserializeTypeX(jo, serializer)); 
      } 
      return new Type1 { Children = children }; 
     } 
     else if (prop.Name == "data.Type2") 
     { 
      return child.ToObject<Type2>(serializer); 
     } 
     else if (prop.Name == "data.Type3") 
     { 
      return child.ToObject<Type3>(serializer); 
     } 
     throw new JsonSerializationException("Unrecognized type: " + prop.Name); 
    } 

    public override bool CanWrite 
    { 
     get { return false; } 
    } 

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) 
    { 
     throw new NotImplementedException(); 
    } 
} 

Вооружившись этим преобразователем вы можете десериализацию к классам, как это:

Root root = JsonConvert.DeserializeObject<Root>(json, new CustomConverter()); 

Затем вы можете сериализовать в новый формат:

JsonSerializerSettings settings = new JsonSerializerSettings 
{ 
    DateFormatString = "yyyy-MM-dd", 
    Formatting = Formatting.Indented 
}; 

Console.WriteLine(JsonConvert.SerializeObject(root, settings)); 

Fiddle: https://dotnetfiddle.net/ESNMLE

0

Не уверен, что это сработает, но вы попытались использовать Newtonsoft.Json для сериализации объекта и включить теги JsonProperty в свойствах класса? Я знаю, что это будет работать при десериализации Json в класс.

<JsonProperty("user_id")> 
Public Property UserID As String 
//Converts Json {user_id: 123} to class.UserID = 123 

сериализовать с Newtonsoft, первым включать Newtonsoft в проект, то

Dim stringJson As String = Newtonsoft.Json.JsonConvert.SerializeObject(root) 
+0

Спасибо вам за ответ.Я попытался, но проблема в том, что все уровни должны быть удалены и заменены, потому что они служат только для того, чтобы различать, какой тип они представляют, где JsonProperty предназначен только для сопоставления между именем класса и именем свойства json. –