处理在C#中保存不同类型的JSON字段 [英] Dealing with JSON field that holds different types in C#
问题描述
我必须阅读一个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
可以是long
或int
s的数组,因此将Doc
转换为类层次结构是有意义的. (如果它是单个long
或long
s的数组,则将它们全部读入一个类是很有意义的.)
Since xx
can either be a long
or an array of int
s, it makes sense to turn Doc
into a class hierarchy. (If it were a single long
or an array of long
s, 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
可以是单个long
或long
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 long
s, 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屋!