简体   繁体   中英

Deserialize JSON array of unnamed arrays using Newtonsoft

I am trying to deserialize a JSON object that has an array of unnamed arrays and am running into some problems. The code I am running to test:

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

and the classes of interest:

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

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

Running the code and trying to deserialize using Newtonsoft I get the following exception:

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.

Adding [JsonArray] to the Triangle class results in the following exception though:

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

What am I missing?

Edit: An important thing I obviously forgot to mention was that I would like to deserialize it into the classes listed in the post for semantic reasons. That is, although deserializing Triangles into List<List<int>> or int[][] would work I would very much prefer to not do so.

With a Custom converter. Deserialise the array to a JArray and select from there.

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

The way your classes are currently set up. JSON.NET will try to deserialize Triangle[] as an array of json object. The most simple solution is to change the type of Triangles to int[,] to make it a 2d array. If you want to keep using Triangle[] you need to use a custom JsonConverter .

Edit: Since you want to keep the Triangle class you need a custom JsonConverter class.

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[]>()
        };
    }
}

To tell JSON.NET to use the TriangleJsonConverter you need to apply JaonArray to the Triangles field instead of JsonProperty .

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM