2016-05-13 30 views
1

我有我使用的VSC#(Windows窗体)的代码保存为一个类下面的XML文件:添加父ID序列化的Object类

<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到子领域 孩子们?(将只有nullid=1节点,否则 每个孩子都有家长ID)
  2. 第二个问题是,在对象类编码后,怎么可能 我插入带有给ID名称所希望的孩子?例如,I 想插入id=4Cname=S112C后面的 节点与id=4

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

让我们假设我要创建一个新的领域,如这需要创建的字符串的值StepHierarchy /用户给出

Step.Hierarchy = // some strings ; 

这意味着我想用ParentId替换它。其原因是因为有时候有,我应该插入两个空节点/组件某些情况下(有没有为它的名称和ID,如下)为一些步骤的孩子

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

其中一个空节点会是另一个的孩子。然后,我将难以给第二个节点(孩子到上述节点)PrentId参考。

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

这就是为什么我要创造这样Hierarchy虚拟场任意值分配给它,并参考ParentId到它,而不是Id。然后每个步骤都有一个非null参考。

如果你有一个想法,将是感激!

+0

@dbc非常感谢为您的评论和答案!在第二个问题中,我的意思是通过搜索给定的Step类来向Step层次结构中插入Step。 – Royeh

回答

1

如何确保child.ParentId在反序列化后总是等于parent.Id

在反序列化之后设置Step.ParentId的自然方法是在OnDeserialized事件中这样做。不幸的是,XmlSerializer does not support deserialization events。鉴于此,您可能需要调查其他设计。

一种可能性是,以取代List<Step>使用自定义集合自动保持当一个孩子被添加到父,沿Maintaining xml hierarchy (ie parent-child) information in objects generated by XmlSerializer线的ParentId参考。遗憾的是,ObservableCollection不适用于此目的,因为the list of old items is not included in the notification event when it is cleared。但是,通过继承System.Collections.ObjectModel.Collection<T>来制造我们自己的产品非常容易。

因此,您的对象模型将成为以下内容。请注意,我已经修改了你的一些属性名称遵循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将每当孩子被添加到父设置,既deserialzation后,在任何应用程序代码。

(如果因任何原因不能Collection<Step>取代你List<Step>,你可以考虑序列化数组代理属性的设置器的ParentId值,沿XML deserialization with parent object reference行。但我是不由自主地想到一个设计将在所有情况下的父ID是优选的。)

我怎样才能通过指定ParentId添加StepStep对象树?

您可以创建递归Linq扩展横穿Step层次,沿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特定的父母,你会怎么做:

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

原型fiddle

更新

如果您不知何故无法新增extension methodsStepSteps,因为它们是嵌套类,你可以添加TraverseSteps()Add()为对象的方法:

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

感谢您的回答。我对第一个问题有错误。 '错误CS0246:无法找到类型或名称空间名称'Collection'。我可以问你的帮忙吗? – Royeh

+0

['Collection Collection '](https://msdn.microsoft.com/en-us/library/ms132397%28v=vs.110%29.aspx)位于命名空间System.Collections.ObjectModel中,因此您需要'使用System.Collections.ObjectModel;' – dbc

+0

对不起,我有它,但有一个错字。顺便说一下第二个问题,我有这个错误:'错误CS1109:扩展方法必须在顶级静态类中定义; StepExtensions是一个嵌套类。我把它从主窗体中删除,但仍然出现错误 – Royeh