处理在C#中包含不同类型的JSON字段

时间:2014-11-25 16:18:12

标签: c# json deserialization

我必须阅读一个JSON文档,它有一个可以包含不同类型的字段。 例如,可以是long或整数数组。我知道我需要使用自定义反序列化器,但我不确定如何。 在下面的示例中,xx字段有时是一个long,否则是一个int数组。 任何有关如何处理此问题的帮助表示赞赏。

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

2 个答案:

答案 0 :(得分:1)

由于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可以是单个longlong数组,那么您可以简化代码如下:

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

答案 1 :(得分:0)

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