使用 Newtonsoft.Json 反序列化字符串数组与同一负载中字符串数组的数组

问题描述 投票:0回答:1

我发现这个答案非常有帮助,但是dbc特别说,

转换器不适用于其项目也被序列化为集合的列表,例如

List<string []>

问题是,我需要反序列化一个数组,该数组可能是字符串数组,也可能是字符串数组的数组。所以这个例子说它在我的情况下不起作用。

例如,

{"values": [ "foo", "bar" ]}

{"values": [ [ "foo", "bar" ], [ "baz", "qux" ] ]}

我正在考虑实现一个自定义类

    public class SingleOrArray<T> : ICollection<T>
    {
        public bool IsArray { get; set; } = false;
        public T Value { get; set; }

        public T[] Values { get { return _items.ToArray() } }

        private List<T> _items = new();

        // etc.
    }

使用如下所示的反序列化对象:

    public class MyObject
    { [JsonConverter(typeof(SingleOrArrayCollectionConverter<SingleOrArray<string[]>, string[]>))]
        public List<SingleOrArray<string[]>> Value { get; set; }
    }

但我不知道如何实现 JsonConverter 来处理这个问题。

这是我的单元测试,它们正在通过,但我需要它来处理这种情况:

string[]

	
json.net
1个回答
0
投票
[Fact] public void SingleItemTest() { // Arrange var testData = "{ \"value\": \"a\" }"; // Act var target = JsonConvert.DeserializeObject<MyObject>(testData); // Assert Assert.False(target!.Value.IsArray); Assert.Equal("a", target!.Value.Value); } [Fact] public void ArrayItemTest1() { // Arrange var testData = "{ \"value\": [\"a\"] }"; // Act var target = JsonConvert.DeserializeObject<MyObject>(testData); // Assert Assert.True(target!.Value.IsArray); Assert.Equal("a", target!.Value.Values[0]); }

,我认为将您的

SingleOrArray<T>
属性定义为
Values
会更容易
List<List<string>>

假设您这样做,您需要决定如何反序列化像
public class Root { [JsonConverter(typeof(SingleOrArray2DListConverter<string>))] public List<List<string>> Values { get; set; } = new(); }

这样的一维数组。 你有两个选择:

首先

,您可以将每个内部项目反序列化为自己的数组:["foo","bar"]

通过这种设计,您可以使用

[["foo"], ["bar"]]

这个答案
如何使用JSON.net处理同一属性的单个项目和数组,方法是将转换器应用于外部列表中的每个项目。 您可以通过设置
SingleOrArrayCollectionConverter<List<string>, string>
:

来做到这一点
JsonPropertyAttribute.ItemConverterType

请注意,通过这种设计,嵌套不一致的 JSON 数组(例如

public partial class Root
{
    [JsonProperty(ItemConverterType = typeof(SingleOrArrayCollectionConverter<List<string>, string>))]
    public List<List<string>> Values { get; set; } = new();
}
)会被自动处理并变成
["foo",["bar"]]

演示小提琴#1 这里

其次,您可以将所有内部项目反序列化为单个内部数组:

[["foo"],["bar"]]

通过这种设计,无法使用链接问题中的转换器,您必须从头开始编写自己的转换器:

[["foo","bar"]]

然后按如下方式应用:

public class SingleOrArray2DListConverter<TItem> : JsonConverter<List<List<TItem>>>
{
    public SingleOrArray2DListConverter() : this(false) { }
    public SingleOrArray2DListConverter(bool canWrite) { this.CanWrite = canWrite; }

    static void ValidateItemContract(IContractResolver resolver)
    {
        var itemContract = resolver.ResolveContract(typeof(TItem));
        if (itemContract is JsonArrayContract)
            throw new JsonSerializationException(string.Format("Item contract type {0} not supported.", itemContract));
    }

    public override List<List<TItem>>? ReadJson(JsonReader reader, Type objectType, List<List<TItem>>? existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        ValidateItemContract(serializer.ContractResolver);
        if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
            return default;
        var outerList = existingValue ?? (List<List<TItem>>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator!();
        if (reader.TokenType == JsonToken.StartArray)
        {
            int startDepth = reader.Depth;
            switch (reader.ReadToContentAndAssert().TokenType)
            {
                case JsonToken.EndArray:
                // Empty 2d array
                break;
                case JsonToken.StartArray:
                {// Nonempty 2d array
                    do
                    {
                        // TODO: decide whether to allow null inner lists.  If you want to allow them, remove the new() 
                        // And change the converter type to `List<List<TItem>?>`
                        outerList.Add(serializer.Deserialize<List<TItem>>(reader) ?? new()); 
                    }
                    while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndArray);
                }
                break;
                default:
                {// Nonempty 1d array
                    var list = new List<TItem>();
                    do
                    {
                        list.Add(serializer.Deserialize<TItem>(reader)!);
                    }
                    while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndArray);
                    outerList.Add(list);
                }
                break;
            }
            // Assert we are positioned at the end of the outer array with the same depth.
            Debug.Assert(reader.TokenType == JsonToken.EndArray && reader.Depth == startDepth);
        }
        else
        {
            // A single non-array item.
            outerList.Add(new() { serializer.Deserialize<TItem>(reader)! });
        }
        return outerList;
    }

    public override bool CanWrite { get; }

    public override void WriteJson(JsonWriter writer, List<List<TItem>>? value, JsonSerializer serializer)
    {
        if (value == null)
            writer.WriteNull();
        else if (value.Count == 1)
        {
            foreach (var item in value)
            {
                serializer.Serialize(writer, item);
                break;
            }
        }
        else
        {
            writer.WriteStartArray();
            foreach (var item in value)
                serializer.Serialize(writer, item);
            writer.WriteEndArray();
        }
    }
}

// Base utilities

public static partial class JsonExtensions
{
    public static JsonReader ReadToContentAndAssert(this JsonReader reader) =>
        reader.ReadAndAssert().MoveToContentAndAssert();

    public static JsonReader MoveToContentAndAssert(this JsonReader reader)
    {
        ArgumentNullException.ThrowIfNull(reader);
        if (reader.TokenType == JsonToken.None)       // Skip past beginning of stream.
            reader.ReadAndAssert();
        while (reader.TokenType == JsonToken.Comment) // Skip past comments.
            reader.ReadAndAssert();
        return reader;
    }

    public static JsonReader ReadAndAssert(this JsonReader reader)
    {
        ArgumentNullException.ThrowIfNull(reader);
        if (!reader.Read())
            throw new JsonReaderException("Unexpected end of JSON stream.");
        return reader;
    }
}

注意,这种设计无法处理

public class Root
{
    [JsonConverter(typeof(SingleOrArray2DListConverter<string>))]
    public List<List<string>> Values { get; set; } = new();
}   
等嵌套不一致的JSON数组,遇到时会抛出异常。

演示小提琴 #2 这里

© www.soinside.com 2019 - 2024. All rights reserved.