【问题标题】:Dealing with JSON field that holds different types in C# [duplicate]在 C# 中处理包含不同类型的 JSON 字段 [重复]
【发布时间】:2014-11-25 16:18:11
【问题描述】:

我必须阅读一个 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();
     }
  }

【问题讨论】:

  • 将值分配给一个字符串并执行一个长的 TryParse,如果失败尝试将其转换为一个数组。
  • 你不能这样做,你会得到一个数组分隔符的异常 [.

标签: c# json deserialization


【解决方案1】:

由于xx 可以是longints 的数组,因此将Doc 转换为类层次结构是有意义的。 (如果它是单个 longlongs 的数组,则将它们全部读入一个类是有意义的。)

您可以使用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 可以是单个longlongs 的数组,您可以将代码简化如下:

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

【讨论】:

  • @GOancea - 添加了简化版本。
  • 我最终根据您的建议使用了一些东西,但避免使用派生类。只需使用 Doc 并根据 item["xx"] 和 item["id"] 在 ReadJson 方法中设置类字段。
  • 和我上面做的差不多。谢谢。不需要写,但很高兴知道。
【解决方案2】:

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-08-02
    • 2013-09-02
    • 1970-01-01
    • 2020-12-06
    • 1970-01-01
    • 1970-01-01
    • 2019-07-28
    • 2011-05-05
    相关资源
    最近更新 更多