问题
I'm working with a third-party API that plays fast and loose with its returned JSON objects. In C#, I'm trying to set up Json.NET to deserialize these JSON objects to classes, but I've run into a hitch: sometimes the same property name will be used with several different schemas, depending on context.
Here's an example of the JSON data structure:
{
"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"
}
]
}
Before discovering this inconsistency, I represented the first example with this 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);
Now, there are two problems with this approach:
- The "data" key has two different property types. Sometimes it's a String and sometimes it's an object. I could create a custom class for the inner object, but then Json.NET would complain about the string version instead.
- When "data" is an object, its property names are inconsistent - note that the second data entry has a property called 19 and the third has one called 21. The structure of these objects is the same, but since their key names are different, I can't directly map them to classes.
The first issue is the more pressing one.
I know that I could solve the issue using JsonExtensionData if necessary, but I'm not sure if this is the best way, and it doesn't provide any compile-time safety in my application.
What is the best way for me to deserialize this JSON into a C# class?
回答1:
You can use a JsonConverter. Here's a fully functioning example:
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();
}
来源:https://stackoverflow.com/questions/50010433/json-net-different-json-schemas-for-a-single-property