Ben*_*son 6 c# json json.net deserialization json-deserialization
我有以下格式的JSON:
{
"users": [
{
"first_name": "John",
"last_name": "Smith",
"vet": [ "FOO", "VET-1" ],
"animals": [ [ "FOO", "ANIMAL-22" ] ]
},
{
"first_name": "Susan",
"last_name": "Smith",
"vet": [ "FOO", "VET-1" ]
}
],
"BAR": {
"VET-1": {
"vet_name": "Acme, Inc",
"vet_id": 456
},
"ANIMAL-22": {
"animal_name": "Fido",
"species": "dog",
"animal_id": 789,
"vet": [ "FOO", "VET-1" ]
}
}
}
Run Code Online (Sandbox Code Playgroud)
某些嵌套对象或多次引用的对象被序列化为引用.
然后,引用的对象包含在BARJSON对象末尾的数组中,并由[ "FOO", "ANIMAL-22" ]数组确定.
(这两个FOO和BAR是静态常数,和ANIMAL-22/ VET-1标识符是半随机的)
不幸的是,这与Json.NET已经序列化/反序列化引用对象的方式不匹配,我可以实现的IReferenceResolver似乎不允许我对行为进行足够的调整(固定使用"$ ref"作为开始).
我也尝试为受影响的属性编写自定义JsonConverter,但我似乎无法获得BAR对根对象属性的引用.
有没有办法可以覆盖Json.NET将上面的JSON反序列化为这种C#类结构?
public class User
{
[JsonProperty("first_name")]
public string FirstName { get; set; }
[JsonProperty("last_name")]
public string LastName { get; set; }
[JsonProperty("vet")]
public Vet Vet { get; set; }
[JsonProperty("animals")]
public List<Animal> Animals { get; set; }
}
public class Vet
{
[JsonProperty("vet_id")]
public int Id { get; set; }
[JsonProperty("vet_name")]
public string Name { get; set; }
}
public class Animal
{
[JsonProperty("animal_id")]
public int Id { get; set; }
[JsonProperty("animal_name")]
public string Name { get; set; }
[JsonProperty("vet")]
public Vet Vet { get; set; }
[JsonProperty("species")]
public string Species { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
编辑#1:虽然我只给出了Animal,但Vet在我的例子中,有很多类型以这种方式引用,我认为我需要一个"通用"或类型无关的解决方案来处理数组结构的任何这种情况[ "FOO", "..." ]而不需要单独编码每个C#类型.
正如@dbc在评论中所说,没有一种简单的方法可以让Json.Net自动处理你的自定义参考格式.也就是说,您可以使用LINQ-to-JSON(JObjects)来解析JSON,并在一个JsonConverter和几个词典的帮助下,解析引用并填充您的类,同时仍然将大部分繁重的工作留给Json.Net .这是我要采取的方法:
创建一个自定义泛型JsonConverter,它可以解码[ "FOO", "<key>" ]引用格式并从提供的字典中返回相应的对象.这是转换器的代码:
public class ReferenceConverter<T> : JsonConverter
{
private Dictionary<string, T> ReferenceDict { get; set; }
public ReferenceConverter(Dictionary<string, T> referenceDict)
{
ReferenceDict = referenceDict;
}
public override bool CanConvert(Type objectType)
{
return objectType == typeof(T);
}
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
JArray array = JArray.Load(reader);
if (array.Count == 2 &&
array[0].Type == JTokenType.String &&
(string)array[0] == "FOO" &&
array[1].Type == JTokenType.String)
{
string key = (string)array[1];
T obj;
if (ReferenceDict.TryGetValue(key, out obj))
return obj;
throw new JsonSerializationException("No " + typeof(T).Name + " was found with the key \"" + key + "\".");
}
throw new JsonSerializationException("Reference had an invalid format: " + array.ToString());
}
public override bool CanWrite
{
get { return false; }
}
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
throw new NotImplementedException();
}
}
Run Code Online (Sandbox Code Playgroud)将JSON解析为a JObject并Vets从BARJSON部分构建字典.
JObject data = JObject.Parse(json);
Dictionary<string, Vet> vets = data["BAR"]
.Children<JProperty>()
.Where(jp => jp.Value["vet_id"] != null)
.ToDictionary(jp => jp.Name, jp => jp.Value.ToObject<Vet>());
Run Code Online (Sandbox Code Playgroud)构建的字典Animals从BAR该JSON的部分,使用ReferenceConverter<T>和字典从第2步解决Vet的引用.
JsonSerializer serializer = new JsonSerializer();
serializer.Converters.Add(new ReferenceConverter<Vet>(vets));
Dictionary<string, Animal> animals = data["BAR"]
.Children<JProperty>()
.Where(jp => jp.Value["animal_id"] != null)
.ToDictionary(jp => jp.Name, jp => jp.Value.ToObject<Animal>(serializer));
Run Code Online (Sandbox Code Playgroud)最后,users从JSON 反序列化列表,再次使用ReferenceConverter<T>加两个字典(实际上现在有两个转换器实例,每个字典一个)来解析所有引用.
serializer.Converters.Add(new ReferenceConverter<Animal>(animals));
List<User> users = data["users"].ToObject<List<User>>(serializer);
Run Code Online (Sandbox Code Playgroud)这里有完整的演示:https://dotnetfiddle.net/uUuy7v