Десериализовать имя свойства, которое может иметь разные типы - PullRequest
1 голос
/ 04 апреля 2020

Я получаю данные от провайдера, и они могут выглядеть следующим образом:

{
    "data": [
        {
            "propertyNames":[
                {
                    "a":"a1",
                    "b":"b1",
                    ...
                    "z":"z1"
                },
                {
                    "a":"a2",
                    "b":"b2",
                    ...
                    "z":"z2"
                },
            ],
            ...
            "otherProperty": "abc"
        },
        {
            "propertyNames":{
                "1": {
                    "a":"a1",
                    "b":"b1",
                    ...
                    "z":"z1"
                },
                "2": {
                    "a":"a2",
                    "b":"b2",
                    ...
                    "z":"z2"
                },
            },
            ...
            "otherProperty": "bce"
        }
    ]
}

Таким образом, propertyNames может быть следующих типов:

[JsonProperty("propertyNames")]
Dictionary<string, MyObject> PropertyNames {get;set;}

[JsonProperty("propertyNames")]
List<MyObject> PropertyNames {get;set;}

Как я могу десериализовать это

Ответы [ 2 ]

2 голосов
/ 05 апреля 2020

Предполагая, что имена свойств внутри объекта "propertyNames" являются целыми числами, вы можете определить свою модель данных следующим образом, используя ListToDictionaryConverter<T> от этот ответ до Показать JSON массив объектов в datagridview :

Модель данных:

public class MyObject
{
    public string a { get; set; }
    public string b { get; set; }
    public string z { get; set; }
}

public class Datum
{
    [JsonConverter(typeof(ListToDictionaryConverter<MyObject>))]
    public List<MyObject> propertyNames { get; set; }
    public string otherProperty { get; set; }
}

public class RootObject
{
    public List<Datum> data { get; set; }
}

Преобразователь копируется как есть, без изменений. Он преобразует JSON объект

{
   "1":{
      "a":"a1",
      "b":"b1",
      "z":"z1"
   },
   "2":{
      "a":"a2",
      "b":"b2",
      "z":"z2"
   }
}

в List<MyObject> со значениями в индексах 1 и 2 и null в индексе ноль.

Демонстрационная скрипка # 1 здесь .

В качестве альтернативы , если вы предпочитаете, чтобы ваш propertyNames был типа Dictionary<int, MyObject>, вы можете изменить модель и преобразователь следующим образом:

public class Datum
{
    [JsonConverter(typeof(IntegerDictionaryToListConverter<MyObject>))]
    public Dictionary<int, MyObject> propertyNames { get; set; }
    public string otherProperty { get; set; }
}

public class IntegerDictionaryToListConverter<T> : JsonConverter where T : class
{
    // From this answer https://stackoverflow.com/a/41559688/3744182
    // To https://stackoverflow.com/questions/41553379/display-json-object-array-in-datagridview
    public override bool CanConvert(Type objectType)
    {
        return typeof(Dictionary<int, T>).IsAssignableFrom(objectType);
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
            return null;
        var dictionary = existingValue as IDictionary<int, T> ?? (IDictionary<int, T>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
        if (reader.TokenType == JsonToken.StartObject)
            serializer.Populate(reader, dictionary);
        else if (reader.TokenType == JsonToken.StartArray)
        {
            var list = serializer.Deserialize<List<T>>(reader);
            for (int i = 0; i < list.Count; i++)
                dictionary.Add(i, list[i]);
        }
        else
        {
            throw new JsonSerializationException(string.Format("Invalid token {0}", reader.TokenType));
        }
        return dictionary;
    }

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

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

Demo fiddle # 2 здесь .

Если имена свойств не всегда целочисленные, вы можете слегка изменить преобразователь, чтобы десериализовать в Dictionary<string, T>.

2 голосов
/ 04 апреля 2020

На самом деле есть несколько вариантов того, как вы можете подойти к этому.

Например, вы можете использовать JObject как тип

[JsonProperty("propertyNames")]
JObject PropertyNames {get;set;}

К сожалению, вам придется написать вниз сложную логику c для анализа информации.

Вы также можете создать собственный JsonConverter

public class DynamicTypeConverter: JsonConverter<DynamicType>
{
    public override void WriteJson(JsonWriter writer, Version value, JsonSerializer serializer)
    {
    }

    public override DynamicType ReadJson(JsonReader reader, Type objectType, Version existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        var obj = new DynamicType();
        // Least fun part, compute & assign values to properties
        // Logic depends if you are going to use JObject.Load(reader) or reader.Read() with reader.Value and TokenType
        return obj;
    }
}

public class DynamicType
{
  Dictionary<string, MyObject> PropertyNamesDict {get;set;}
  List<MyObject> PropertyNamesList {get;set;}
}
...