我有以下格式的 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" ]
}
}
}
某些嵌套对象或多次引用的对象被序列化为引用。
然后引用的对象包含在 JSON 对象末尾的 BAR
数组中,并由 [ "FOO", "ANIMAL-22"]
数组。
(FOO
和BAR
都是静态常量,ANIMAL-22
/VET-1
标识符是半随机的)
不幸的是,这与 Json.NET 已经 serializes/deserializes referenced objects 的方式不匹配,而我可以实现的 IReferenceResolver 似乎不允许我充分调整行为(首先是 it's fixed to use "$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; }
}
编辑 #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(); } }
将 JSON 解析为
JObject
并建立Vets
的字典来自BAR
JSON 的一部分。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>());
构建字典
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));
最后,反序列化
users
来自 JSON 的列表,再次使用ReferenceConverter<T>
加上两个字典(现在实际上有两个转换器实例,每个字典一个)来解析所有引用。serializer.Converters.Add(new ReferenceConverter<Animal>(animals)); List<User> users = data["users"].ToObject<List<User>>(serializer);
完整演示在这里:https://dotnetfiddle.net/uUuy7v
关于c# - 使用自定义格式作为引用时,如何使用 Json.NET 通过引用反序列化对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/43048335/