Десериализовать массив безымянных массивов JSON с помощью Newtonsoft - PullRequest
0 голосов
/ 04 ноября 2019

Я пытаюсь десериализовать объект JSON, который имеет массив неназванных массивов, и столкнулся с некоторыми проблемами. Код, который я запускаю для тестирования:

var json = "{ \"Triangles\": [[1337],[1338],[1339]]}";
var mesh  = JsonConvert.DeserializeObject<Mesh>(json);

и представляющие интерес классы:

public class Mesh
{
    [JsonProperty]
    public Triangle[] Triangles { get; set; }
}

public class Triangle
{
    [JsonProperty]
    public int[] Indices { get; set; }
}

При запуске кода и попытке десериализации с помощью Newtonsoft я получаю следующее исключение:

Newtonsoft.Json.JsonSerializationException
  HResult=0x80131500
  Message=Cannot deserialize the current JSON array (e.g. [1,2,3]) into type 'ConsoleApp1.Triangle' 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 'triangles[0]', line 1, position 17.

Добавление [JsonArray] в класс Triangle приводит к следующему исключению:

Newtonsoft.Json.JsonSerializationException
  HResult=0x80131500
  Message=Cannot create and populate list type ConsoleApp1.Triangle. Path 'Triangles[0]', line 1, position 17.

Чего мне не хватает?

Редактировать: Важная вещь, которую я, очевидно, забылупоминалось, что я хотел бы десериализовать его в классы, перечисленные в посте по семантическим причинам. То есть, хотя десериализация треугольников в List<List<int>> или int[][] сработает, я бы очень предпочел не делать этого.

Ответы [ 2 ]

0 голосов
/ 04 ноября 2019

с пользовательским конвертером. Десериализовать массив в JArray и выбрать оттуда.

var json = "{ \"Triangles\": [[1337,1400],[1338],[1339]]}";
var mesh = JsonConvert.DeserializeObject<Mesh>(json);

public class Mesh
{
    [JsonConverter(typeof(MyConverter))]
    public Triangle[] Triangles { get; set; }
}   
public class Triangle
{
    [JsonProperty]
    public int[] Indices { get; set; }
}

public class MyConverter : JsonConverter
{
    public override bool CanConvert(Type objectType)
    {
        return true;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        if (reader.TokenType == JsonToken.Null)
        {
            return null;
        }
        var result =
            JArray.Load(reader)
            .Select(x =>
                new Triangle { Indices = x.Select(y => (int)y).ToArray() }
            );

        return result.ToArray();
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}
0 голосов
/ 04 ноября 2019

Способ настройки ваших классов. JSON.NET попытается десериализовать Triangle[] как массив объекта json. Наиболее простым решением является изменение типа Triangles на int[,], чтобы сделать его двумерным массивом. Если вы хотите продолжать использовать Triangle[], вам нужно использовать пользовательский JsonConverter.

Редактировать: поскольку вы хотите сохранить класс Triangle, вам нужен пользовательский класс JsonConverter.

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

public class TriangleJsonConverter : JsonConverter<Triangle>
{
    // Called when Triangle is written
    public override void WriteJson(JsonWriter writer, Triangle value, JsonSerializer serializer)
    {
        // Uses JsonSerializer to write the int[] to the JsonWriter
        serializer.Serialize(writer, value.Indices);
    }

    // Called when Triangle is read
    public override Triangle ReadJson(JsonReader reader, Type objectType, Triangle existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        // Reads a json array (JArray) from the JsonReader
        var array = JArray.Load(reader);
        // Creates a new Triangle.
        return new Triangle
        {
            // converts the json array to an int[]
            Indices = array.ToObject<int[]>()
        };
    }
}

Чтобы указать JSON.NET использовать TriangleJsonConverter, вам нужно применить JaonArray к полю Triangles вместо JsonProperty.

public class Mesh
{
    [JsonArray(ItemConverterType = typeof(TriangleJsonConverter))]
    public Triangle[] Triangles { get; set; }
}
...