简体   繁体   English

反序列化JSON以包含对C#中另一个反序列化对象的引用

[英]Deserialize JSON to type containing reference to another deserialized object in C#

I have a following model: 我有以下模型:

public interface IEntity
{
    string Id { get; }
}

public class EntityParent : IEntity
{
    public string Id { get; }

    public EntityChild EntityChild { get; }

    [JsonConstructor]
    public EntityParent(string id, EntityChild entityChild)
    {
        Id = id;
        EntityChild = entityChild;
    }
}

public class EntityChild : IEntity
{
    public string Id { get; }

    public int Age { get; }

    [JsonConstructor]
    public EntityChild(string id, int age)
    {
        Id = id;
        Age = age;
    }
}

Next I have some JSON that I need to deserialize to collections of the above types: 接下来,我有一些JSON需要反序列化为上述类型的集合:

{
            "Children":
            [
                {
                    "Id"          : "Billy",
                    "Age"         : 42
                }
            ],

            "Parents" :
            [
                {
                    "Id"          : "William",
                    "EntityChild" : "Billy"
                }
            ]
}

Eventually I want to have a list of EntityChild ren and a list of EntityParent s that will (optionally) contain references to objects in the first list , or at least references to instances of EntityChild . 最终,我想要一个EntityChild ren列表和EntityParent列表,它们将(可选)包含对第一个列表中的对象的引用,或者至少包含对EntityChild实例的EntityChild I have attempted to write a custom JsonConverter (I am using Newtonsoft.Json 9.0.1 NuGet package), where in the ReadJson() method I am looking for a child with specific Id , like so: 我试图编写一个自定义的JsonConverter (我正在使用Newtonsoft.Json 9.0.1 NuGet包),在其中,我在ReadJson()方法中寻找一个具有特定Id的孩子,如下所示:

public class ParentConverter<TEntity> : JsonConverter where TEntity : IEntity
{
    private readonly IEnumerable<TEntity> _children;

    public ParentConverter(IEnumerable<TEntity> children)
    {
        _children = children;
    }

    public override object ReadJson(JsonReader reader, Type objectType, object existingValue,
        JsonSerializer serializer)
    {
        JObject jObject = JObject.Load(reader);

        TEntity target = _children.FirstOrDefault(d => d.Id == jObject["Id"].ToString());

        serializer.Populate(jObject.CreateReader(), target);

        return target;
    }

    public override bool CanConvert(Type objectType)
    {
        return typeof(TEntity).IsAssignableFrom(objectType);
    }

    public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
    {
        throw new NotImplementedException();
    }
}

Here's a simple test: 这是一个简单的测试:

public class JsonTest
{
    const string Json = @"
        {
            ""Children"": [
                {
                    ""Id""          : ""Billy"",
                    ""Age""         : 42
                }
            ],

            ""Parents"" : [
                {
                    ""Id""          : ""William"",
                    ""EntityChild"" : ""Billy""
                }
            ]
        }";

    public static void Main()
    {
        JObject jObject = JObject.Parse(Json);
        var children =
            JsonConvert.DeserializeObject<List<EntityChild>>(jObject["Children"].ToString());
        var parents =
            JsonConvert.DeserializeObject<List<EntityParent>>(jObject["Parents"].ToString(),
                new ParentConverter<EntityChild>(children));
    }
}

children are deserialized properly, but parents throw a JsonReaderException when attempting to call JObject.Load(reader); children已正确反序列化,但是parents JsonReaderException在尝试调用JObject.Load(reader);时抛出JsonReaderException JObject.Load(reader); in ReadJson() , saying " Error reading JObject from JsonReader. Current JsonReader item is not an object: String. Path '[0].EntityChild'. " ReadJson()中说:“ 从JsonReader读取JObject时出错。当前的JsonReader项不是对象:字符串。路径'[0] .EntityChild'。

Does anyone know how I should go about it? 有人知道我应该怎么做吗? Thanks in advance. 提前致谢。

Edit: Updated the EntityChild with extra property to emphasize that the property on EntityParent has to be of EntityChild type, and not a string . 编辑:更新了带有额外属性的EntityChild以强调EntityParent上的属性必须为EntityChild类型,而不是string

Entity parent should be as below: 实体父级应如下所示:

public class EntityParent : IEntity
{
    public string Id { get; }

    public string EntityChild { get; }

    [JsonConstructor]
    public EntityParent(string id, string entityChild)
    {
        Id = id;
        EntityChild = entityChild;
    }
}

And in main() change as below: 并在main()中进行如下更改:

var parents = JsonConvert.DeserializeObject<List<EntityParent>>(jObject["Parents"].ToString());

and it works. 而且有效。

The following is sort of a workaround using LINQ, where I basically join the parent JObjects with children JObjects to build the parent and the child: 以下是使用LINQ的一种变通方法,在该方法中,我基本上将父JObject与子JObject连接在一起以构建父和子:

    public static void Main()
    {
        JObject jObject = JObject.Parse(Json);

        IEnumerable<EntityParent> parents =
            from parent in jObject["Parents"]
            join child in jObject["Children"] on parent["EntityChild"] equals child["Id"]
            select
                new EntityParent(
                    parent["Id"].ToString(),
                    new EntityChild(
                        child["Id"].ToString(),
                        child["Age"].ToObject<int>()));
    }

If children list would exist already, then the join could be performed on that list instead, like so: 如果子列表已经存在,则可以在该列表上执行联接,如下所示:

    public static void Main()
    {
        JObject jObject = JObject.Parse(Json);

        var children =
            JsonConvert.DeserializeObject<List<EntityChild>>(jObject["Children"].ToString());

        IEnumerable<EntityParent> parents =
            from parent in jObject["Parents"]
            join child in children on parent["EntityChild"] equals child.Id
            select new EntityParent(parent["Id"].ToString(), child);
    }

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

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