[英]How can I use Json.NET to deserialize objects by reference, when using a custom format for the reference?
我有以下格式的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已经序列化/反序列化引用对象的方式不匹配,我可以实现的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; }
}
编辑#1:虽然我在我的例子中只提供Animal
和Vet
,但是有很多类型以这种方式引用,我认为我需要一个“通用”或类型无关的解决方案来处理任何这种类型的数组结构[ "FOO", "..." ]
无需单独为每个C#类型编码。
正如@dbc在评论中所说,没有一种简单的方法可以使Json.Net自动处理您的自定义参考格式。 也就是说,您可以使用LINQ-to-JSON (JObjects)来解析JSON,并在JsonConverter
和几个词典的帮助下解析引用并填充您的类,同时仍然将大部分繁重的工作留给Json。净。 这是我要采取的方法:
创建一个自定义通用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
并从JSON的BAR
部分构建Vets
字典。
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>());
使用ReferenceConverter<T>
和步骤2中的字典来解析Vet
引用,从JSON的BAR
部分构建Animals
字典。
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));
最后,再次使用ReferenceConverter<T>
加上两个字典(现在实际上是两个转换器实例,每个字典一个)来解析所有引用,从而对JSON中的users
列表进行反序列化。
serializer.Converters.Add(new ReferenceConverter<Animal>(animals)); List<User> users = data["users"].ToObject<List<User>>(serializer);
这里有完整的演示: https : //dotnetfiddle.net/uUuy7v
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.