C# NewtonSoft 单一对象或数组 JsonConverter 不工作,没有错误
C# NewtonSoft Single Object or Array JsonConverter not working, no errors
我正在尝试反序列化一些 JSON,它有时是一个数组,有时是一个对象。由于项目限制,它必须被推广。我查看了这个 Stack Overflow 问题 (Deserializing JSON when sometimes array and sometimes object),但该代码不适用于我的单元测试。它也没有错误,并且调试器显示在转换器内部它正在 returning 对象,就像它应该的那样。
当我说它不起作用时,它反序列化所有内容,直到它到达有问题的 JSON 元素,然后它只是序列化为一个空数组,如下所示:
"a": []
这是我的测试JSON:
{
"somefile": [
{
"name": "Bob",
"a": {
"asub": "Bob",
"atracking": "Bob",
"adate": " Bob"
}
},
{
"name": "Bob",
"a": [
{
"asub": "Bob",
"atracking": "Bob",
"adate": "Bob"
},
{
"asub": "Bob",
"atracking": "Bob",
"adate": "Bob"
}
]
}
]
}
这是我的 C# 数据模型,底部有转换器:
public class DummyDataModel
{
public List<SomeFile> someFile { get; } = new List<SomeFile>();
public partial class SomeFile
{
public string name { get; set; } = "";
[JsonConverter(typeof(SingleObjectOrArrayJsonConverter<SomeA>))]
public List<SomeA> a { get; } = new List<SomeA>();
//public SomeA a { get; set; } = new SomeA();
}
public partial class SomeA
{
public string asub { get; set; } = "";
public string atracking { get; set; } = "";
public string adate { get; set; } = "";
}
internal class SingleObjectOrArrayJsonConverter<T> : JsonConverter<List<T>> where T : class, new()
{
public override void WriteJson(JsonWriter writer, List<T> value, JsonSerializer serializer)
{
serializer.Serialize(writer, value.Count == 1 ? (object)value.Single() : value);
}
public override List<T> ReadJson(JsonReader reader, Type objectType, List<T> existingValue, bool hasExistingValue, JsonSerializer serializer)
{
return reader.TokenType switch
{
JsonToken.StartObject => new List<T> { serializer.Deserialize<T>(reader) },
JsonToken.StartArray => serializer.Deserialize<List<T>>(reader),
_ => throw new ArgumentOutOfRangeException($"Converter does not support JSON token type {reader.TokenType}.")
};
}
}
}
关于为什么它可以毫无错误地完成每个步骤但仍然 return 没有任何提示的任何提示?
这是 运行 此测试的行(我使用的是 WPF 和 C# 8.0)
DummyDataModel uTest = JsonConvert.DeserializeObject<DummyDataModel>(unitJSON);
txtResult.Text = $"Did Test:\n{JsonConvert.SerializeObject(uTest, Formatting.Indented)}";
您的 a
属性 是 get-only,因此在您的 ReadJson()
方法中您需要填充传入的 List<T> existingValue
列表(这将是当前的 属性 值)而不是创建一个新列表:
internal class SingleObjectOrArrayJsonConverter<T> : JsonConverter<List<T>> where T : class, new()
{
public override void WriteJson(JsonWriter writer, List<T> value, JsonSerializer serializer) =>
// avoid possibility of infinite recursion by wrapping the List<T> with AsReadOnly()
serializer.Serialize(writer, value.Count == 1 ? (object)value.Single() : value.AsReadOnly());
public override List<T> ReadJson(JsonReader reader, Type objectType, List<T> existingValue, bool hasExistingValue, JsonSerializer serializer)
{
existingValue ??= new ();
switch (reader.TokenType)
{
case JsonToken.StartObject: existingValue.Add(serializer.Deserialize<T>(reader)); break;
case JsonToken.StartArray: serializer.Populate(reader, existingValue); break;
default: throw new ArgumentOutOfRangeException($"Converter does not support JSON token type {reader.TokenType}.");
};
return existingValue;
}
}
另请参阅 this answer to How to handle both a single item and an array for the same property using JSON.net,它显示了一个类似但更通用的转换器。
演示 fiddle here.
我正在尝试反序列化一些 JSON,它有时是一个数组,有时是一个对象。由于项目限制,它必须被推广。我查看了这个 Stack Overflow 问题 (Deserializing JSON when sometimes array and sometimes object),但该代码不适用于我的单元测试。它也没有错误,并且调试器显示在转换器内部它正在 returning 对象,就像它应该的那样。
当我说它不起作用时,它反序列化所有内容,直到它到达有问题的 JSON 元素,然后它只是序列化为一个空数组,如下所示:
"a": []
这是我的测试JSON:
{
"somefile": [
{
"name": "Bob",
"a": {
"asub": "Bob",
"atracking": "Bob",
"adate": " Bob"
}
},
{
"name": "Bob",
"a": [
{
"asub": "Bob",
"atracking": "Bob",
"adate": "Bob"
},
{
"asub": "Bob",
"atracking": "Bob",
"adate": "Bob"
}
]
}
]
}
这是我的 C# 数据模型,底部有转换器:
public class DummyDataModel
{
public List<SomeFile> someFile { get; } = new List<SomeFile>();
public partial class SomeFile
{
public string name { get; set; } = "";
[JsonConverter(typeof(SingleObjectOrArrayJsonConverter<SomeA>))]
public List<SomeA> a { get; } = new List<SomeA>();
//public SomeA a { get; set; } = new SomeA();
}
public partial class SomeA
{
public string asub { get; set; } = "";
public string atracking { get; set; } = "";
public string adate { get; set; } = "";
}
internal class SingleObjectOrArrayJsonConverter<T> : JsonConverter<List<T>> where T : class, new()
{
public override void WriteJson(JsonWriter writer, List<T> value, JsonSerializer serializer)
{
serializer.Serialize(writer, value.Count == 1 ? (object)value.Single() : value);
}
public override List<T> ReadJson(JsonReader reader, Type objectType, List<T> existingValue, bool hasExistingValue, JsonSerializer serializer)
{
return reader.TokenType switch
{
JsonToken.StartObject => new List<T> { serializer.Deserialize<T>(reader) },
JsonToken.StartArray => serializer.Deserialize<List<T>>(reader),
_ => throw new ArgumentOutOfRangeException($"Converter does not support JSON token type {reader.TokenType}.")
};
}
}
}
关于为什么它可以毫无错误地完成每个步骤但仍然 return 没有任何提示的任何提示?
这是 运行 此测试的行(我使用的是 WPF 和 C# 8.0)
DummyDataModel uTest = JsonConvert.DeserializeObject<DummyDataModel>(unitJSON);
txtResult.Text = $"Did Test:\n{JsonConvert.SerializeObject(uTest, Formatting.Indented)}";
您的 a
属性 是 get-only,因此在您的 ReadJson()
方法中您需要填充传入的 List<T> existingValue
列表(这将是当前的 属性 值)而不是创建一个新列表:
internal class SingleObjectOrArrayJsonConverter<T> : JsonConverter<List<T>> where T : class, new()
{
public override void WriteJson(JsonWriter writer, List<T> value, JsonSerializer serializer) =>
// avoid possibility of infinite recursion by wrapping the List<T> with AsReadOnly()
serializer.Serialize(writer, value.Count == 1 ? (object)value.Single() : value.AsReadOnly());
public override List<T> ReadJson(JsonReader reader, Type objectType, List<T> existingValue, bool hasExistingValue, JsonSerializer serializer)
{
existingValue ??= new ();
switch (reader.TokenType)
{
case JsonToken.StartObject: existingValue.Add(serializer.Deserialize<T>(reader)); break;
case JsonToken.StartArray: serializer.Populate(reader, existingValue); break;
default: throw new ArgumentOutOfRangeException($"Converter does not support JSON token type {reader.TokenType}.");
};
return existingValue;
}
}
另请参阅 this answer to How to handle both a single item and an array for the same property using JSON.net,它显示了一个类似但更通用的转换器。
演示 fiddle here.