Дать имя массиву массивов

GinjaNinja спросил: 28 апреля 2018 в 08:47 в: c#

Я использую Newtonsoft для разбора некоторого JSon в .Net-тип. В json содержится массив массивов, называемый "данными". Я хотел бы сделать каждый массив в массиве данных собственным типом, но я не уверен, как это сделать.

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

    public class TheData
    {
        [JsonProperty(PropertyName = "data")]
        public List<object> dataItems { get; set; }
    }

Использование:

        string json =
            "{\"data\":[[\"20180511\",1094391],[\"20180504\",1097315],[\"20180427\",1100221],[\"20180420\",1094455],[\"20180413\",1093023]]}";        var myObj = JsonConvert.DeserializeObject<TheData>(json);

Это работает нормально, однако я хотел бы изменить тип dataItems из списка в список, как показано ниже:

    public class TheData
    {
        [JsonProperty(PropertyName = "data")]
        public List<DataItem> dataItems { get; set; }
    }public class DataItem
{
    public string deldate { get; set; }
    public int value { get; set; }
}

Однако это приводит к исключению:

Newtonsoft.Json.JsonSerializationException occurred
  HResult=-2146233088
  Message=Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'CE.FOTools.Feeds2.EIA.DataItem' because the type requires a JSON object (e.g. {"name":"value"}) to deserialize correctly.
To fix this error either change the JSON to a JSON object (e.g. {"name":"value"}) or change the deserialized type to an array or a type that implements a collection interface (e.g. ICollection, IList) like List<T> that can be deserialized from a JSON array. JsonArrayAttribute can also be added to the type to force it to deserialize from a JSON array.
Path 'data[0]', line 1, position 10.

Сообщение об ошибке указывает, что мой желаемый результат может быть невозможен, но может ли кто-нибудь предложить, как исправить это? Я не контролирую формат JSON (unles я использую строку после ее получения). Я использую .Net 4.5, если это имеет значение.

1 ответ

Есть решение
Evk ответил: 28 апреля 2018 в 09:33

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

class DataItemConverter : JsonConverter<DataItem> {
    public override void WriteJson(JsonWriter writer, DataItem value, JsonSerializer serializer) {
        // if item can be null - handle that
        writer.WriteStartArray();
        writer.WriteValue(value.deldate);
        writer.WriteValue(value.value);
        writer.WriteEndArray();
    }    public override DataItem ReadJson(JsonReader reader, Type objectType, DataItem existingValue, bool hasExistingValue, JsonSerializer serializer) {
        var ar = serializer.Deserialize<List<object>>(reader);
        // perform some checks for length and data types, omitted here
        var result = new DataItem();
        result.deldate = (string) ar[0];
        result.value = Convert.ToInt32(ar[1]);
        return result;
    }
}

Затем укажите, что это преобразование должно использоваться при оформлении вашего типа:

[JsonConverter(typeof(DataItemConverter))]
public class DataItem {
    public string deldate { get; set; }
    public int value { get; set; }
}

И после этого он должен работать как вы ожидаете.

Если общий тип JsonConverter<> недоступен в вашей версии Json.NET, используйте не общий:

class DataItemConverter : JsonConverter {
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) {
        var item = (DataItem) value;
        // if null is possible - handle that
        writer.WriteStartArray();
        if (item != null) {
            writer.WriteValue(item.deldate);
            writer.WriteValue(item.value);
        }        writer.WriteEndArray();
    }    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) {
        var ar = serializer.Deserialize<List<object>>(reader);
        // perform some checks for length and data types, omitted here
        var result = new DataItem();
        result.deldate = (string) ar[0];
        result.value = Convert.ToInt32(ar[1]);
        return result;
    }    public override bool CanConvert(Type objectType) {
        return objectType == typeof(DataItem);
    }
}
GinjaNinja ответил: 28 апреля 2018 в 09:24
Большое спасибо за ваш ответ. Я думаю, что я могу использовать более старую версию Newtonsoft, поскольку это не компилируется. Я потрачу немного времени на обновление и отправку отчетов.
Evk ответил: 28 апреля 2018 в 09:28
@GinjaNinja Я обновил ответ с версией, которая должна работать в более старых версиях.
GinjaNinja ответил: 28 апреля 2018 в 10:55
Работает в обаянии. Большое спасибо за усилия.