2016-05-13 37 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