处理在 C# 中保存不同类型的 JSON 字段

本文关键字:同类型 JSON 字段 保存 处理 | 更新日期: 2023-09-27 18:32:00

我必须读取一个JSON文档,该文档有一个可以包含不同类型的字段。例如,可以是长整型或整数数组。我知道我需要使用自定义反序列化程序,但不确定如何使用。在下面的示例中,xx 字段有时很长,否则是一个整数数组。关于如何处理这个问题的任何帮助,我们将不胜感激。

        static void JsonTest() {
           const string json = @"
  {
     'Code': 'XYZ',
     'Response': {
        'Type' : 'S',
        'Docs': [
           { 
              'id' : 'test1',
              'xx' : 1
           },
           { 
              'id' : 'test2',
              'xx' : [1, 2, 4, 8]
           },
        ]
     }
  }";
           A a;
           try {
              a = JsonConvert.DeserializeObject<A>(json);
           }
           catch( Exception ex ) {
              Console.Error.WriteLine(ex.Message);
           }
        }
        public class A {
           public string Code;
           public TResponse Response;
        }
        public class TResponse {
           public string Type;
           public List<Doc> Docs;
        }
        public class Doc {
           public string id;
           public int[] xx;
        }

我基于以下建议的实现(将数组从 int 更改为 long):

  [JsonConverter(typeof(DocConverter))]
  public class Doc {
     public string id;
     public long[] xx;
  }
  public class DocConverter : JsonConverter {
     public override bool CanWrite { get { return false; } }
     public override bool CanConvert( Type objectType ) {
        return typeof(Doc).IsAssignableFrom(objectType);
     }
     public override object ReadJson( JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer ) {
        JObject item = JObject.Load(reader);
        Doc doc = new Doc();
        doc.id = item["id"].ToObject<string>();
        if( item["xx"].Type == JTokenType.Long )
           doc.xx = new [] { item["xx"].ToObject<long>() };
        else
           doc.xx = item["xx"].ToObject<long[]>();
        return doc;
     }
     public override void WriteJson( JsonWriter writer, object value, JsonSerializer serializer ) {
        throw new NotImplementedException();
     }
  }

处理在 C# 中保存不同类型的 JSON 字段

由于xx可以是long数组或int数组,因此将Doc转换为类层次结构是有意义的。 (如果是单个longlong数组,将它们全部读取到单个类中是有意义的。

您可以使用 JsonConverter 来执行此操作,如下所示:

[JsonConverter(typeof(DocConverter))]
public abstract class Doc
{
    public string id;
}
[JsonConverter(typeof(NoConverter))] // Prevents infinite recursion when converting a class instance known to be of type DocSingle
public class DocSingle : Doc
{
    public long xx;
}
[JsonConverter(typeof(NoConverter))] // Prevents infinite recursion when converting a class instance known to be of type DocList
public class DocList : Doc
{
    public int[] xx;
}
public class DocConverter : JsonConverter
{
    public override bool CanWrite { get { return false; } }
    public override bool CanConvert(Type objectType)
    {
        return typeof(Doc).IsAssignableFrom(objectType);
    }
    public override object ReadJson(JsonReader reader, 
        Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject item = JObject.Load(reader);
        if (item["xx"].Type == JTokenType.Integer)
        {
            return item.ToObject<DocSingle>();
        }
        else
        {
            return item.ToObject<DocList>();
        }
    }
    public override void WriteJson(JsonWriter writer, 
        object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}
public class NoConverter : JsonConverter
{
    public override bool CanRead { get { return false; } }
    public override bool CanWrite { get { return false; } }
    public override bool CanConvert(Type objectType)
    {
        return false;
    }
    public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

更新

顺便说一下,如果你愿意简化你的数据模型,说xx可以是单个long也可以是long数组,你可以按如下方式简化代码:

[JsonConverter(typeof(DocConverter))]
public sealed class Doc
{
    public string id;
    public long[] xx;
}
public class DocConverter : JsonConverter
{
    public override bool CanWrite { get { return true; } }
    public override bool CanConvert(Type objectType)
    {
        return typeof(Doc).IsAssignableFrom(objectType);
    }
    public override object ReadJson(JsonReader reader,
        Type objectType, object existingValue, JsonSerializer serializer)
    {
        JObject item = JObject.Load(reader);
        var doc = new Doc();
        JToken id = item["id"];
        if (id != null)
            doc.id = id.ToString();
        JToken xx = item["xx"];
        if (xx != null)
        {
            if (xx.Type == JTokenType.Integer)
            {
                var val = (long)xx;
                doc.xx = new long[] { val };
            }
            else if (xx.Type == JTokenType.Array)
            {
                var val = xx.ToObject<long[]>();
                doc.xx = val;
            }
            else
            {
                Debug.WriteLine("Unknown type of JToken for '"xx'": " + xx.ToString());
            }
        }
        return doc;
    }
    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        var doc = (Doc)value;
        writer.WriteStartObject();
        writer.WritePropertyName("id");
        writer.WriteValue(doc.id);
        var xx = doc.xx;
        if (xx != null)
        {
            writer.WritePropertyName("xx");
            if (xx.Length == 1)
            {
                writer.WriteValue(xx[0]);
            }
            else
            {
                writer.WriteStartArray();
                foreach (var x in xx)
                {
                    writer.WriteValue(x);
                }
                writer.WriteEndArray();
            }
        }
        writer.WriteEndObject();
    }
}

你有一个字符串,试试 json。包含("'类型':"S'")。然后将其反序列化为正确的模型。