处理在C#中保存不同类型的JSON字段 [英] Dealing with JSON field that holds different types in C#

查看:125
本文介绍了处理在C#中保存不同类型的JSON字段的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我必须阅读一个JSON文档,该文档的字段可以包含不同的类型. 例如,可以是long或整数数组.我知道我将需要使用自定义解串器,但不确定如何使用. 在下面的示例中,xx字段有时是一个长整数,否则是一个整数数组. 感谢您提供有关如何处理此问题的帮助.

I have to read a JSON document, which has a field that can contain different types. For example can be either a long or an array of integers. I know I will need to use a custom deserializer, but am not sure how. In the example below the xx field sometimes is a long, otherwise an array of ints. Any help on how to deal with this is appreciated.

        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):


My implementation based on the suggestion below (changed array to long from int):

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

推荐答案

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

Since xx can either be a long or an array of ints, it makes sense to turn Doc into a class hierarchy. (If it were a single long or an array of longs, it would make sense to read them all into a single class.)

您可以使用JsonConverter来做到这一点,就像这样:

You can do this by using a JsonConverter, like so:

[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 s的数组,则可以将代码简化如下:

incidentally, if you're willing to simplify your data model to say that xx can either be a single long or an array of longs, you can simplify the code as follows:

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

这篇关于处理在C#中保存不同类型的JSON字段的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆