将父 ID 添加到序列化作为对象类 [英] Adding the Parent id to Serialization as Object class

查看:28
本文介绍了将父 ID 添加到序列化作为对象类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下 XML 文件,我使用 VSC#(windows forms) 代码将其保存为类:

I have the following XML file which I am using the VSC#(windows forms) code to save it as a class:

<Steps >
  <Step id ="1" Name="S1">
    <Step id ="2" Name="S11">
      <Step id ="3" Name="S111" />
      <Step id ="4" Name="S112" />
        <Step id ="5" Name="S1121" />
    </Step >
    <Step id ="6" Name="S12" />
  </Step >
</Steps >

我写的代码:

[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class Steps
{
    [System.Xml.Serialization.XmlElementAttribute("Step")]
    public List<Step> Step { get; set; }
}
[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class Step
{
    [System.Xml.Serialization.XmlElementAttribute("Step")]
    public List<Step> Step1 { get; set; }
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string name { get; set; }
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string id { get; set; }
    [System.Xml.Serialization.XmlAttributeAttribute()]
    public string ParentID { get; set; }
}

我有两个问题:

  1. 如何将 ParentID 放入子字段中孩子?(对于 id=1 的节点,只有 null,否则每个孩子都有自己的父母 ID)
  2. 第二个问题是,在对象类中编码后,如何我插入一个想要的孩子并给出 id 名称?例如,我想在后面插入一个 id=4Cname=S112C 的孩子id=4 的节点?
  1. How can I get the ParentID into the child field for children?(there would be only null for node with id=1, otherwise each child has its parents id)
  2. The second question is that after coding in object class, how could I insert a desired child with giving the id name? For example, I would like to insert a child with id=4C and name=S112C after node with id=4?

更新:(回答两个问题后)

让我们假设我想在 Step 中创建一个作为 Hierarchy 的新字段,它采用用户创建/提供的字符串值

Let's we assume that I want to create a new field as Hierarchy in the Step which takes values of string created/given by user

Step.Hierarchy = // some strings ;

这意味着我想用 ParentId 替换它.原因是因为有时在某些情况下我应该插入两个空节点/组件(没有名称和ID,如下所示)作为子节点进行某些步骤

It means I want to replace it with ParentId. The reason is that because sometimes there are some situations which I should insert two empty nodes/components(There is no name and Id for it, as below) as a child for some steps

steps.Add(new Step { Id = " ", Name = " " }, "4");

其中一个空节点将成为另一个节点的子节点.然后我将难以为第二个节点(上述节点的子节点)提供 PrentId 引用.

where one empty node will be child of other one. Then I will have difficulty for giving PrentId reference for the second node(child to the above node).

steps.Add(new Step { Id = " ", Name = " " }, " ");

这就是为什么我想创建一个像 Hierarchy 这样的虚拟字段来为其分配一个任意值并将 ParentId 引用到它而不是 Id>.然后每个 Step 都有一个非 null 引用.

This is why I want to create a virtual field like Hierarchy to assign an arbitrary value to it and refer ParentId to it instead of Id. Then each Step has a non null reference.

如果你有一个想法会很感激!!

If you have an idea that would be thankful!!

推荐答案

如何保证反序列化后 child.ParentId 始终等于 parent.Id ?

How can I ensure that child.ParentId always equals parent.Id after deserializing?

在反序列化后设置 Step.ParentId 的自然方法是在 OnDeserialized 事件.不幸的是,XmlSerializer 不支持反序列化事件.鉴于此,您可能需要研究替代设计.

The natural approach to setting Step.ParentId after deserialization would be to do so in an OnDeserialized event. Unfortunately, XmlSerializer does not support deserialization events. Given that, you may need to investigate an alternate design.

一种可能性是将您的 List 替换为自定义集合,该集合在添加子项时自动维护 ParentId 引用父级,遵循 维护 xml 层次结构XmlSerializer 生成的对象中的(即父子)信息.不幸的是,ObservableCollection 不适合这个目的,因为 旧物品列表清除时不包含在通知事件中.但是,通过对 System.Collections 进行子类化,可以很容易地创建我们自己的.ObjectModel.Collection.

One possibility is to replace your List<Step> with a custom collection that automatically maintains the ParentId reference when a child is added to a parent, along the lines of Maintaining xml hierarchy (ie parent-child) information in objects generated by XmlSerializer. Unfortunately, ObservableCollection is not suitable for this purpose, because the list of old items is not included in the notification event when it is cleared. However, it's quite easy to make our own by subclassing System.Collections.ObjectModel.Collection<T>.

因此,您的对象模型将如下所示.请注意,我已经修改了您的一些属性名称以遵循 c# 命名指南:

Thus, your object model would become the following. Note that I have modified some of your property names to follow c# naming guidelines:

[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class Steps
{
    readonly ChildCollection<Step> steps;

    public Steps()
    {
        this.steps = new ChildCollection<Step>();
        this.steps.ChildAdded += (s, e) =>
        {
            if (e.Item != null)
                e.Item.ParentId = null;
        };
    }

    [System.Xml.Serialization.XmlElementAttribute("Step")]
    public Collection<Step> StepList { get { return steps; } }
}

[System.SerializableAttribute()]
[System.ComponentModel.DesignerCategoryAttribute("code")]
[System.Xml.Serialization.XmlTypeAttribute(AnonymousType = true)]
[System.Xml.Serialization.XmlRootAttribute(Namespace = "", IsNullable = false)]
public partial class Step
{
    readonly ChildCollection<Step> steps;

    public Step()
    {
        this.steps = new ChildCollection<Step>();
        this.steps.ChildAdded += (s, e) =>
        {
            if (e.Item != null)
                e.Item.ParentId = this.Id;
        };
    }

    [System.Xml.Serialization.XmlElementAttribute("Step")]
    public Collection<Step> StepList { get { return steps; } }

    [System.Xml.Serialization.XmlAttributeAttribute("Name")]
    public string Name { get; set; }
    [System.Xml.Serialization.XmlAttributeAttribute("id")]
    public string Id { get; set; }
    [System.Xml.Serialization.XmlAttributeAttribute("ParentID")]
    public string ParentId { get; set; }
}

public class ChildCollectionEventArgs<TChild> : EventArgs
{
    public readonly TChild Item;

    public ChildCollectionEventArgs(TChild item)
    {
        this.Item = item;
    }
}

public class ChildCollection<TChild> : Collection<TChild>
{
    public event EventHandler<ChildCollectionEventArgs<TChild>> ChildAdded;

    public event EventHandler<ChildCollectionEventArgs<TChild>> ChildRemoved;

    void OnRemoved(TChild item)
    {
        var removed = ChildRemoved;
        if (removed != null)
            removed(this, new ChildCollectionEventArgs<TChild>(item));
    }

    void OnAdded(TChild item)
    {
        var added = ChildAdded;
        if (added != null)
            added(this, new ChildCollectionEventArgs<TChild>(item));
    }

    public ChildCollection() : base() { }

    protected override void ClearItems()
    {
        foreach (var item in this)
            OnRemoved(item);
        base.ClearItems();
    }

    protected override void InsertItem(int index, TChild item)
    {
        OnAdded(item);
        base.InsertItem(index, item);
    }

    protected override void RemoveItem(int index)
    {
        if (index >= 0 && index < Count)
        {
            OnRemoved(this[index]);
        }
        base.RemoveItem(index);
    }

    protected override void SetItem(int index, TChild item)
    {
        OnAdded(item);
        base.SetItem(index, item);
    }
}

现在 ParentId 将在将子项添加到父项时设置,无论是在反序列化之后还是在任何应用程序代码中.

Now ParentId will be set whenever a child is added to a parent, both after deserialzation, and in any applications code.

(如果由于某种原因你不能Collection替换你的List,你可以考虑序列化一个数组代理属性并在 setter 中设置 ParentId 值,沿着 带有父对象引用的 XML 反序列化.但我认为在所有情况下自动设置父 id 的设计更可取.)

(If for whatever reason you cannot replace your List<Step> with a Collection<Step>, you could consider serializing an array proxy property and setting the ParentId values in the setter, along the lines of XML deserialization with parent object reference. But I think a design that automatically sets the parent id in all situations is preferable.)

如何通过指定 ParentIdStep 添加到 Step 对象树中?

How can I add a Step to a tree of Step objects by specifying ParentId?

您可以创建遍历 Step 层次结构的递归 Linq 扩展,沿着 使用 LINQ 进行高效的图遍历 - 消除递归:

You could create recursive Linq extensions that traverse the Step hierarchy, along the lines of Efficient graph traversal with LINQ - eliminating recursion:

public static class StepExtensions
{
    public static IEnumerable<Step> TraverseSteps(this Steps root)
    {
        if (root == null)
            throw new ArgumentNullException();
        return RecursiveEnumerableExtensions.Traverse(root.StepList, s => s.StepList);
    }

    public static IEnumerable<Step> TraverseSteps(this Step root)
    {
        if (root == null)
            throw new ArgumentNullException();
        return RecursiveEnumerableExtensions.Traverse(root, s => s.StepList);
    }

    public static bool TryAdd(this Steps root, Step step, string parentId)
    {
        foreach (var item in root.TraverseSteps())
            if (item != null && item.Id == parentId)
            {
                item.StepList.Add(step);
                return true;
            }
        return false;
    }

    public static void Add(this Steps root, Step step, string parentId)
    {
        if (!root.TryAdd(step, parentId))
            throw new InvalidOperationException(string.Format("Parent {0} not found", parentId));
    }
}

public static class RecursiveEnumerableExtensions
{
    // Rewritten from the answer by Eric Lippert https://stackoverflow.com/users/88656/eric-lippert
    // to "Efficient graph traversal with LINQ - eliminating recursion" http://stackoverflow.com/questions/10253161/efficient-graph-traversal-with-linq-eliminating-recursion
    // to ensure items are returned in the order they are encountered.

    public static IEnumerable<T> Traverse<T>(
        T root,
        Func<T, IEnumerable<T>> children)
    {
        yield return root;

        var stack = new Stack<IEnumerator<T>>();
        try
        {
            stack.Push((children(root) ?? Enumerable.Empty<T>()).GetEnumerator());

            while (stack.Count != 0)
            {
                var enumerator = stack.Peek();
                if (!enumerator.MoveNext())
                {
                    stack.Pop();
                    enumerator.Dispose();
                }
                else
                {
                    yield return enumerator.Current;
                    stack.Push((children(enumerator.Current) ?? Enumerable.Empty<T>()).GetEnumerator());
                }
            }
        }
        finally
        {
            foreach (var enumerator in stack)
                enumerator.Dispose();
        }
    }

    public static IEnumerable<T> Traverse<T>(
        IEnumerable<T> roots,
        Func<T, IEnumerable<T>> children)
    {
        return from root in roots
               from item in Traverse(root, children)
               select item;
    }
}

他们通过 ID 将孩子添加到特定的父母,你会这样做:

Them to add a child to a specific parent by ID, you would do:

steps.Add(new Step { Id = "4C", Name = "S112C" }, "4");

原型 fiddle.

更新

如果您在将扩展方法添加到StepSteps 因为它们是嵌套类,您可以添加 TraverseSteps()Add() 作为对象方法:

If you somehow are having trouble adding extension methods to Step and Steps because they are nested classes, you could add TraverseSteps() and Add() as object methods:

public partial class Step
{
    public IEnumerable<Step> TraverseSteps()
    {
        return RecursiveEnumerableExtensions.Traverse(this, s => s.StepList);
    }
}

public partial class Steps
{
    public IEnumerable<Step> TraverseSteps()
    {
        return RecursiveEnumerableExtensions.Traverse(StepList, s => s.StepList);
    }

    public bool TryAdd(Step step, string parentId)
    {
        foreach (var item in TraverseSteps())
            if (item != null && item.Id == parentId)
            {
                item.StepList.Add(step);
                return true;
            }
        return false;
    }

    public void Add(Step step, string parentId)
    {
        if (!TryAdd(step, parentId))
            throw new InvalidOperationException(string.Format("Parent {0} not found", parentId));
    }
}

这篇关于将父 ID 添加到序列化作为对象类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆