【问题标题】:How to serialize and deserialize a JSON object with duplicate property names in a specific order?如何以特定顺序序列化和反序列化具有重复属性名称的 JSON 对象?
【发布时间】:2021-06-21 13:59:21
【问题描述】:

我需要创建一个 C# 类来匹配这个带有精确括号的 json。

{
    "data": {
        "a": "4",
        "b": "2",
        "c": "3",
        "a": "444",
    },
}

System.Collections.Generic.Dictionary<string, string> 可以,但它不允许多个条目具有相同的键,所以这行不通。

List<T> 使用自定义数据类、string[2]、元组或 valueTuples 或 KeyValuePair<string, string> 创建不同的 json,其中每个条目获取“item1”、“item1”或不同的括号。

如何反序列化和序列化这个确切的 JSON?这是我必须支持的客户“标准”。

【问题讨论】:

标签: c# json json.net


【解决方案1】:

虽然在技术上没有格式错误,但最新的 JSON RFC RFC 8259:

不推荐具有重复属性名称的 JSON 对象

一个对象结构表示为一对围绕零个或多个名称/值对(或成员)的大括号...

名称都是唯一的对象是可互操作的,因为接收该对象的所有软件实现都将就名称-值映射达成一致。 当对象中的名称不唯一时,接收此类对象的软件的行为是不可预测的。许多实现仅报告姓氏/值对。其他实现报告错误或无法解析对象,并且一些实现报告所有名称/值对,包括重复项。

您可能希望向您的客户建议另一种 JSON 格式,例如单个键值对对象的数组。

话虽如此,如果您必须序列化和反序列化具有重复属性的对象,Json.NET 不会开箱即用地执行此操作,您必须创建custom JsonConverter 手动完成。由于 JSON 对象的值都是相同的类型(此处为字符串),您可以绑定到 List<KeyValuePair<string, string>>

首先,定义如下模型:

public class Model
{
    [JsonConverter(typeof(KeyValueListAsObjectConverter<string>))]
    public List<KeyValuePair<string, string>> data { get; } = new ();
}

以及以下通用JsonConverter&lt;List&lt;KeyValuePair&lt;string, TValue&gt;&gt;&gt;

public class KeyValueListAsObjectConverter<TValue> : JsonConverter<List<KeyValuePair<string, TValue>>>
{
    public override List<KeyValuePair<string, TValue>> ReadJson(JsonReader reader, Type objectType, List<KeyValuePair<string, TValue>> existingValue, bool hasExistingValue, JsonSerializer serializer)
    {
        if (reader.MoveToContentAndAssert().TokenType == JsonToken.Null)
            return null;
        reader.AssertTokenType(JsonToken.StartObject);
        var list = existingValue ?? (List<KeyValuePair<string, TValue>>)serializer.ContractResolver.ResolveContract(objectType).DefaultCreator();
        while (reader.ReadToContentAndAssert().TokenType != JsonToken.EndObject)
        {
            var name = (string)reader.AssertTokenType(JsonToken.PropertyName).Value;
            var value = serializer.Deserialize<TValue>(reader.ReadToContentAndAssert());
            list.Add(new KeyValuePair<string, TValue>(name, value));
        }
        return list;
    }

    public override void WriteJson(JsonWriter writer, List<KeyValuePair<string, TValue>> value, JsonSerializer serializer)
    {
        writer.WriteStartObject();
        foreach (var pair in value)
        {
            writer.WritePropertyName(pair.Key);
            serializer.Serialize(writer, pair.Value);
        }
        writer.WriteEndObject();
    }
}

public static partial class JsonExtensions
{
    public static JsonReader AssertTokenType(this JsonReader reader, JsonToken tokenType) => 
        reader.TokenType == tokenType ? reader : throw new JsonSerializationException(string.Format("Unexpected token {0}, expected {1}", reader.TokenType, tokenType));
    
    public static JsonReader ReadToContentAndAssert(this JsonReader reader) =>
        reader.ReadAndAssert().MoveToContentAndAssert();

    public static JsonReader MoveToContentAndAssert(this JsonReader reader)
    {
        if (reader == null)
            throw new ArgumentNullException();
        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)
    {
        if (reader == null)
            throw new ArgumentNullException();
        if (!reader.Read())
            throw new JsonReaderException("Unexpected end of JSON stream.");
        return reader;
    }
}

您将能够反序列化和重新序列化问题中显示的 JSON。

演示小提琴here.

【讨论】:

  • 感谢您以一个对我和那些追随我类似问题的人有所帮助的答案结束本文。
猜你喜欢
  • 2014-08-26
  • 1970-01-01
  • 1970-01-01
  • 2021-12-09
  • 1970-01-01
  • 2015-09-24
  • 2017-12-30
  • 2014-04-24
  • 2021-07-22
相关资源
最近更新 更多