【问题标题】:Store/retrieve child-parent relationship with JsonConverter使用 JsonConverter 存储/检索子父关系
【发布时间】:2017-10-16 15:43:26
【问题描述】:

我有一个带有子对象数组的 JSON 结构(包括 POCO 类),如下所示:

“目的”: [ { "名称": "TestA", “儿童对象”:[ { “名称”:“TestB” “儿童对象”:[ { “名称”:“TestC” ... } ] } ]

反序列化时,我想保留对父级的引用 我刚刚创建的对象。

但我必须在填充子对象之前获取此引用。(在填充子对象的那一刻,我必须让父对象结构/引用可访问)

我尝试过使用自定义 JsonConverter,但是 我找不到存储或检索这种关系的方法。

【问题讨论】:

  • 您是否尝试将PreserveReferencesHandling 设置为Objects?如果这样做,则不需要转换器;引用将通过特殊的 $id$ref 元属性保存在 JSON 中。
  • @BrianRogers,我考虑过将“父对象”属性添加到“对象类”中。这样我就可以得到对父母的引用。您刚才所说的这些参考资料是否适合这种用途? (如果它可以作为对 JSON 文件上的父节点的引用)
  • 是的,您的子对象可以引用父对象,反之亦然,如果您在序列化和反序列化时都使用该设置,它应该可以工作。
  • 您能否edit 分享您的类型的(最好是简化的)版本——即minimal reproducible example?我很困惑哪些类型对应于上面的 JSON,哪些类型在创建过程中需要父级。
  • @dbc,完成了

标签: c# json json.net


【解决方案1】:

与其将其定义为序列化问题(如何序列化和反序列化对父级的反向引用),不如将其定义为类设计问题,即

给定父母和孩子的层次结构,如何确保在将孩子添加到父母时自动正确设置孩子对父母的反向引用

一旦以这种方式定义并解决了问题,就应该在反序列化和编程数据创建过程中确保正确性,因为父反向引用永远不需要序列化或反序列化。

实现这一点的一种方法是定义Collection<T> 的自定义子类,它会自动设置和清除父级反向引用。

首先,定义如下接口和集合:

public interface IHasParent<TParent> where TParent : class
{
    TParent Parent { get; }

    void OnParentChanging(TParent newParent);
}

public class ChildCollection<TParent, TChild> : Collection<TChild>
    where TChild : IHasParent<TParent>
    where TParent : class
{
    readonly TParent parent;

    public ChildCollection(TParent parent)
    {
        this.parent = parent;
    }

    protected override void ClearItems()
    {
        foreach (var item in this)
        {
            if (item != null)
                item.OnParentChanging(null);
        }
        base.ClearItems();
    }

    protected override void InsertItem(int index, TChild item)
    {
        if (item != null)
            item.OnParentChanging(parent);
        base.InsertItem(index, item);
    }

    protected override void RemoveItem(int index)
    {
        var item = this[index];
        if (item != null)
            item.OnParentChanging(null);
        base.RemoveItem(index);
    }

    protected override void SetItem(int index, TChild item)
    {
        var oldItem = this[index];
        if (oldItem != null)
            oldItem.OnParentChanging(null);
        if (item != null)
            item.OnParentChanging(parent);
        base.SetItem(index, item);
    }
}

然后定义您的MyObjectRootObject 类型如下:

public class MyObject : IHasParent<MyObject>
{
    readonly ChildCollection<MyObject, MyObject> childObjects;

    public MyObject() { this.childObjects = new ChildCollection<MyObject, MyObject>(this); }

    public string Name { get; set; }

    public IList<MyObject> ChildObjects { get { return childObjects; } }

    #region IHasParent<MyObject> Members

    [JsonIgnore]
    public MyObject Parent { get; private set; }

    public void OnParentChanging(MyObject newParent)
    {
        Parent = newParent;
    }

    #endregion

    // Added to suppress serialization of empty ChildObjects collections to JSON.
    public bool ShouldSerializeChildObjects() { return childObjects.Count > 0; }
}

public class RootObject
{
    public RootObject() { this.Object = new List<MyObject>(); }

    public List<MyObject> Object { get; set; }
}

注意事项:

  • MyObject 中的集合 IList&lt;MyObject&gt; ChildObjects 只能获取。 Json.NET(以及 XmlSerializer 就此而言)可以成功反序列化一个仅获取的预分配集合。

  • ShouldSerializeChildObjects() 方法是可选的,可防止对空的ChildObjects [] 数组值进行序列化。

  • 由于ObservableCollection&lt;T&gt; 本身是Collection&lt;T&gt; 的子类,如果您在添加或删除项目时需要通知,您可以选择它作为ChildCollection&lt;TParent, TChild&gt; 的基类。

  • Parent 属性标有[JsonIgnore] 以防止其序列化。

示例fiddle 包括一些基本的单元测试。

【讨论】:

    【解决方案2】:

    你不需要 JsonConverter。

    您可以创建代表您的 json 的 POCO 类,如下所示:

        public class OstacolisRuntime
        {
            public int CodiceOstacolo { get; set; }
            public int TipoOstacolo { get; set; }
            public int Tipologia { get; set; }
            public string Nome { get; set; }
            public double PosizioneX { get; set; }
            public double PosizioneY { get; set; }
            public double PosizioneZ { get; set; }
            public double AngoloX { get; set; }
            public double AngoloY { get; set; }
            public double AngoloZ { get; set; }
            public double ScalaX { get; set; }
            public double ScalaY { get; set; }
            public double ScalaZ { get; set; }
            public List<SubOggetto> SubOggettos { get; set; } //sub
        }
    
    
    
    
        public class SubOggetto
        {
            public string Immagine { get; set; }
            public int Tipologia { get; set; }
            public string Nome { get; set; }
            public double PosizioneX { get; set; }
            public double PosizioneY { get; set; }
            public double PosizioneZ { get; set; }
            public double AngoloX { get; set; }
            public double AngoloY { get; set; }
            public double AngoloZ { get; set; }
            public double ScalaX { get; set; }
            public double ScalaY { get; set; }
            public double ScalaZ { get; set; }
            public List<SubOggetto> SubOggettos { get; set; } //recursive relashioship
        }
    
    
        public class RootObject
        {
            public List<OstacolisRuntime> OstacolisRuntime { get; set; }
        }
    

    反序列化你的json:

      var o= JsonConvert.DeserializeObject<RootObject>(json);
    

    您可以查看complete source code

    【讨论】:

    • M.哈桑。我已经有这些课程了。我必须在反序列化时找到一种方法来获得这种关系。子节点必须能够在反序列化时知道父节点。
    【解决方案3】:

    为了更清楚地理解 dbc 的答案,让我简化一下。

    让我们以 RootObject 的名为 MyObject 的项目设置父项为例:

    {
        "Object":[
            {
                "Name": "TestA"
            }
        ]
    }
    

    定义集合:

    public class Items : Collection<MyObject>
    {
        private RootObject Owner;
    
        public Items(RootObject owner)
        {
            Owner = owner;
        }
    
        protected override void InsertItem(int index, MyObject item)
        {
            item.Parent = Owner;
            base.InsertItem(index, item);
        }
    }
    

    定义MyObjectRootObject

    public class MyObject
    {
        [JsonIgnore]
        public RootObject Parent { get; set; }
    
        public string Name { get; set; }
    }
    
    public class RootObject
    {
        public RootObject() { ChildObjects = new Items(this); }
    
        public Items ChildObjects { get; }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-27
      • 1970-01-01
      • 2015-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多