将JSON反序列化为包含对C#中另一个反序列化对象的引用的类型

时间:2016-07-13 10:54:37

标签: c# json deserialization

我有以下型号:

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;
    }
}

接下来,我需要将一些JSON反序列化为上述类型的集合:

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

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

最终我希望有一个EntityChild ren列表和一个EntityParent列表,它们(可选)包含对第一个列表中对象的引用,或者至少引用{{1}个实例的引用1}}。我试图编写一个自定义EntityChild(我正在使用 Newtonsoft.Json 9.0.1 NuGet包),在JsonConverter方法中我正在寻找一个具有特定性的孩子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();
    }
}

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尝试在parents中调用JsonReaderException时抛出JObject.Load(reader);,说&#34; 错误从JsonReader读取JObject。当前的JsonReader项不是对象:String。路径&#39; [0] .EntityChild&#39;。&#34;

有谁知道我应该怎么做?提前谢谢。

修改:使用额外属性更新了ReadJson(),以强调EntityChild上的媒体资源必须为EntityParent类型,不是串

2 个答案:

答案 0 :(得分:2)

实体父母应如下所示:

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

    public string EntityChild { get; }

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

并在main()更改如下:

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

它有效。

答案 1 :(得分:0)

以下是一种使用LINQ的解决方法,我基本上将子JObject与子JOASEs连接起来构建父级和子级:

    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>()));
    }

如果已存在子列表,则可以在该列表上执行连接,如下所示:

    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);
    }