简体   繁体   English

在使用自定义格式作为参考时,如何使用Json.NET通过引用反序列化对象?

[英]How can I use Json.NET to deserialize objects by reference, when using a custom format for the reference?

I have JSON in the following format: 我有以下格式的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" ]
        }
    }
}

Some nested objects, or objects referenced more than once, are serialized as references. 某些嵌套对象或多次引用的对象被序列化为引用。

The referenced objects are then included in the BAR array at the end of the JSON object, and identified in place by the [ "FOO", "ANIMAL-22" ] array. 然后,引用的对象包含在JSON对象末尾的BAR数组中,并由[ "FOO", "ANIMAL-22" ]数组进行识别。

(Both FOO and BAR are static constants, and the ANIMAL-22 / VET-1 identifiers are semi-random) FOOBAR都是静态常量, ANIMAL-22 / VET-1标识符是半随机的)

Unfortunately, this doesn't match how Json.NET already serializes/deserializes referenced objects and the IReferenceResolver I could implement doesn't seem to allow me to adjust the behaviour enough ( it's fixed to use "$ref" for a start). 不幸的是,这与Json.NET已经序列化/反序列化引用对象的方式不匹配,我可以实现的IReferenceResolver似乎不允许我对行为进行足够的调整( 固定使用“$ ref”作为开始)。

I've also tried writing a custom JsonConverter for the properties affected, but I can't seem to get a reference to the BAR property of the root object. 我也尝试为受影响的属性编写自定义JsonConverter ,但我似乎无法获得对根对象的BAR属性的引用。

Is there any way I can override Json.NET to deserialize the JSON above into this kind of C# class structure? 有没有办法可以覆盖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; }
}

Edit #1: Although I give only Animal and Vet in my example, there are a large number of types referenced in this way and I think I need a 'generic' or type-agnostic solution that would handle any such occurrence of the array structure [ "FOO", "..." ] without needing to code for each C# type individually. 编辑#1:虽然我在我的例子中只提供AnimalVet ,但是有很多类型以这种方式引用,我认为我需要一个“通用”或类型无关的解决方案来处理任何这种类型的数组结构[ "FOO", "..." ]无需单独为每个C#类型编码。

As @dbc said in the comments, there is not an easy way to make Json.Net automatically handle your custom reference format. 正如@dbc在评论中所说,没有一种简单的方法可以使Json.Net自动处理您的自定义参考格式。 That said, you can use LINQ-to-JSON (JObjects) to parse the JSON, and with the help of a JsonConverter and a couple of dictionaries, resolve the references and populate your classes while still leaving most of the heavy lifting to Json.Net. 也就是说,您可以使用LINQ-to-JSON (JObjects)来解析JSON,并在JsonConverter和几个词典的帮助下解析引用并填充您的类,同时仍然将大部分繁重的工作留给Json。净。 Here's the approach I would take: 这是我要采取的方法:

  1. Create a custom generic JsonConverter which can decode the [ "FOO", "<key>" ] reference format and return the corresponding object from a provided dictionary. 创建一个自定义通用JsonConverter ,它可以解码[ "FOO", "<key>" ]引用格式并从提供的字典中返回相应的对象。 Here is the code for the converter: 这是转换器的代码:

     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(); } } 
  2. Parse the JSON into a JObject and build a dictionary of Vets from the BAR section of the JSON. 将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>()); 
  3. Build a dictionary of Animals from the BAR section of the JSON, using the ReferenceConverter<T> and the dictionary from step 2 to resolve Vet references. 使用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)); 
  4. Finally, deserialize the users list from the JSON, again using the ReferenceConverter<T> plus the two dictionaries (so actually two converter instances now, one per dictionary) to resolve all the references. 最后,再次使用ReferenceConverter<T>加上两个字典(现在实际上是两个转换器实例,每个字典一个)来解析所有引用,从而对JSON中的users列表进行反序列化。

     serializer.Converters.Add(new ReferenceConverter<Animal>(animals)); List<User> users = data["users"].ToObject<List<User>>(serializer); 

Full demo here: https://dotnetfiddle.net/uUuy7v 这里有完整的演示: https//dotnetfiddle.net/uUuy7v

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM