使用 Newtonsoft 反序列化未命名数组的 JSON 数组

Posted

技术标签:

【中文标题】使用 Newtonsoft 反序列化未命名数组的 JSON 数组【英文标题】:Deserialize JSON array of unnamed arrays using Newtonsoft 【发布时间】:2020-02-29 14:54:04 【问题描述】:

我正在尝试反序列化一个 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.

我错过了什么?

编辑:我显然忘记提及的一件重要事情是,出于语义原因,我想将其反序列化为帖子中列出的类。也就是说,虽然将 Triangles 反序列化为 List&lt;List&lt;int&gt;&gt;int[][] 会起作用,但我非常不想这样做。

【问题讨论】:

Json 中的Triangles 属性是一个数组数组。你可以这样修复它:public List&lt;List&lt;int&gt;&gt; Triangles get; set; 您的类的序列化结果将类似于"Triangles":["Indices":[1,2],"Indices":[3],"Indices":[4]]。您必须反序列化为 List&lt;List&lt;int&gt;&gt; 并通过简单的选择选择正确的类或编写自定义解析器。 你所拥有的是一个int数组。序列化的字符串应该更像"Triangles":["Indices":[1,2,3],"Indices":[1,2,3],"Indices":[1,2,3]] @DragandDrop 我错过了补充说,出于语义原因,我更愿意将三角形保留为一个类。我想自定义解析器是我必须采用的方式。 一个代表你得到的数据的类。一个类来表示您将使用的数据。我建议在两者之间进行选择。自定义解析器可能很难。我很确定我的记事本中有一个类似的解析器问题,但我找不到。 【参考方案1】:

使用自定义转换器。将数组反序列化为 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();
    

【讨论】:

【参考方案2】:

您的课程目前的设置方式。 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; 

【讨论】:

还在JsonProperty:[JsonProperty(ItemConverterType = typeof(TriangleJsonConverter))]

以上是关于使用 Newtonsoft 反序列化未命名数组的 JSON 数组的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 Newtonsoft.Json 反序列化 JSON 数组

如何使用 Newtonsoft.Json 正确反序列化数组中的嵌套对象? [复制]

Newtonsoft.JSON 在反序列化数组中被双引号包围的对象时窒息

Newtonsoft.Json 序列化和反序列化 以及时间格式 2

JSON Newtonsoft C# 反序列化数组

c#开发中使用Newtonsoft.Json反序列化数组求解?