Json.NET:单个 属性 的不同 JSON 架构
Json.NET: Different JSON schemas for a single property
我正在与第三方 API 合作,该第三方 API 可以快速、随意地处理其返回的 JSON 对象。在 C# 中,我尝试设置 Json.NET 以将这些 JSON 对象反序列化为 classes,但我 运行 陷入困境:有时相同 属性 名称将根据上下文与几种不同的模式一起使用。
这里有一个 JSON 数据结构的例子:
{
"examples": [{
"data": "String data",
"type": "foo"
}, {
"data": {
"name": "Complex data",
"19": {
"owner": "Paarthurnax"
}
},
"type": "complex"
}, {
"data": {
"name": "Differently complex data",
"21": {
"owner": "Winking Skeever"
}
},
"type": "complex"
}
]
}
在发现这个不一致之前,我用这个 class:
表示第一个例子
public class Example {
[JsonProperty("data")]
public string Data {get; set;}
[JsonProperty("type"]
public string DataType {get; set;}
}
# In the main method
Example deserializedObject = JsonConvert.DeserializeObject<Example>(stringData);
现在,这种方法有两个问题:
- "data" 键有两种不同的 属性 类型。有时它是一个字符串,有时它是一个对象。我可以为内部对象创建一个自定义 class,但是 Json.NET 会抱怨字符串版本。
- 当 "data" 是一个对象时,它的 属性 名称不一致 - 请注意,第二个数据条目有一个 属性 称为 19,第三个有一个称为 21。结构这些对象是相同的,但由于它们的键名不同,我无法将它们直接映射到 classes.
第一个问题比较紧迫
我知道我可以在必要时使用 JsonExtensionData 解决问题,但我不确定这是否是最好的方法,而且它不会在我的应用程序中提供任何编译时安全性。
将此 JSON 反序列化为 C# class 的最佳方法是什么?
您可以使用 JsonConverter。这是一个功能齐全的示例:
public class Example
{
public string StringData { get; set; }
public ComplexData ComplexData { get; set; }
public string Type { get; set; }
}
public class ComplexData
{
public string Name { get; set; }
[JsonProperty("19")]
public Foo Nineteen { get; set; }
[JsonProperty("21")]
public Foo TwentyOne { get; set; }
}
public class Foo
{
public string Owner { get; set; }
}
public class FlexibleJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var examples = new List<Example>();
var obj = JObject.Load(reader);
foreach (var exampleJson in obj["examples"])
{
var example = new Example { Type = (string)exampleJson["type"] };
if (example.Type == "complex")
{
example.ComplexData = exampleJson["data"].ToObject<ComplexData>();
}
else
{
example.StringData = (string)exampleJson["data"];
}
examples.Add(example);
}
return examples.ToArray();
}
public override bool CanConvert(Type objectType)
{
return objectType.IsAssignableFrom(typeof(Example[]));
}
}
private static void Main()
{
var json = @"{
""examples"": [{
""data"": ""String data"",
""type"": ""foo""
}, {
""data"": {
""name"": ""Complex data"",
""19"": {
""owner"": ""Paarthurnax""
}
},
""type"": ""complex""
}, {
""data"": {
""name"": ""Differently complex data"",
""21"": {
""owner"": ""Winking Skeever""
}
},
""type"": ""complex""
}
]
}";
var examples = JsonConvert.DeserializeObject<IEnumerable<Example>>(json, new FlexibleJsonConverter());
foreach (var example in examples)
{
Console.WriteLine($"{example.Type}: {example.StringData ?? example.ComplexData.Nineteen?.Owner ?? example.ComplexData.TwentyOne.Owner}");
}
Console.ReadKey();
}
我正在与第三方 API 合作,该第三方 API 可以快速、随意地处理其返回的 JSON 对象。在 C# 中,我尝试设置 Json.NET 以将这些 JSON 对象反序列化为 classes,但我 运行 陷入困境:有时相同 属性 名称将根据上下文与几种不同的模式一起使用。
这里有一个 JSON 数据结构的例子:
{
"examples": [{
"data": "String data",
"type": "foo"
}, {
"data": {
"name": "Complex data",
"19": {
"owner": "Paarthurnax"
}
},
"type": "complex"
}, {
"data": {
"name": "Differently complex data",
"21": {
"owner": "Winking Skeever"
}
},
"type": "complex"
}
]
}
在发现这个不一致之前,我用这个 class:
表示第一个例子public class Example {
[JsonProperty("data")]
public string Data {get; set;}
[JsonProperty("type"]
public string DataType {get; set;}
}
# In the main method
Example deserializedObject = JsonConvert.DeserializeObject<Example>(stringData);
现在,这种方法有两个问题:
- "data" 键有两种不同的 属性 类型。有时它是一个字符串,有时它是一个对象。我可以为内部对象创建一个自定义 class,但是 Json.NET 会抱怨字符串版本。
- 当 "data" 是一个对象时,它的 属性 名称不一致 - 请注意,第二个数据条目有一个 属性 称为 19,第三个有一个称为 21。结构这些对象是相同的,但由于它们的键名不同,我无法将它们直接映射到 classes.
第一个问题比较紧迫
我知道我可以在必要时使用 JsonExtensionData 解决问题,但我不确定这是否是最好的方法,而且它不会在我的应用程序中提供任何编译时安全性。
将此 JSON 反序列化为 C# class 的最佳方法是什么?
您可以使用 JsonConverter。这是一个功能齐全的示例:
public class Example
{
public string StringData { get; set; }
public ComplexData ComplexData { get; set; }
public string Type { get; set; }
}
public class ComplexData
{
public string Name { get; set; }
[JsonProperty("19")]
public Foo Nineteen { get; set; }
[JsonProperty("21")]
public Foo TwentyOne { get; set; }
}
public class Foo
{
public string Owner { get; set; }
}
public class FlexibleJsonConverter : JsonConverter
{
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var examples = new List<Example>();
var obj = JObject.Load(reader);
foreach (var exampleJson in obj["examples"])
{
var example = new Example { Type = (string)exampleJson["type"] };
if (example.Type == "complex")
{
example.ComplexData = exampleJson["data"].ToObject<ComplexData>();
}
else
{
example.StringData = (string)exampleJson["data"];
}
examples.Add(example);
}
return examples.ToArray();
}
public override bool CanConvert(Type objectType)
{
return objectType.IsAssignableFrom(typeof(Example[]));
}
}
private static void Main()
{
var json = @"{
""examples"": [{
""data"": ""String data"",
""type"": ""foo""
}, {
""data"": {
""name"": ""Complex data"",
""19"": {
""owner"": ""Paarthurnax""
}
},
""type"": ""complex""
}, {
""data"": {
""name"": ""Differently complex data"",
""21"": {
""owner"": ""Winking Skeever""
}
},
""type"": ""complex""
}
]
}";
var examples = JsonConvert.DeserializeObject<IEnumerable<Example>>(json, new FlexibleJsonConverter());
foreach (var example in examples)
{
Console.WriteLine($"{example.Type}: {example.StringData ?? example.ComplexData.Nineteen?.Owner ?? example.ComplexData.TwentyOne.Owner}");
}
Console.ReadKey();
}