将父 ID 添加到序列化作为对象类 [英] Adding the Parent id to Serialization as Object class
问题描述
我有以下 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; }
}
我有两个问题:
- 如何将
ParentID
放入子字段中孩子?(对于id=1
的节点,只有null
,否则每个孩子都有自己的父母 ID) - 第二个问题是,在对象类中编码后,如何我插入一个想要的孩子并给出 id 名称?例如,我想在后面插入一个
id=4C
和name=S112C
的孩子id=4
的节点?
- How can I get the
ParentID
into the child field for children?(there would be onlynull
for node withid=1
, otherwise each child has its parents id) - 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
andname=S112C
after node withid=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.)
如何通过指定 ParentId
将 Step
添加到 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.
更新
如果您在将扩展方法添加到Step
和 Steps
因为它们是嵌套类,您可以添加 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屋!