2016-05-13 12 views
1

Ich habe die folgende XML-Datei, die ich den VSC# (Windows Forms) Code benutzen es als eine Klasse zu speichern:Hinzufügen der Eltern-ID Serialisierung als Objektklasse

<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 > 

Der Code, den ich als schrieb:

[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; } 
} 

ich habe zwei Fragen:

  1. Wie kann ich die ParentID in das Kind Feld für bekommen Kinder? (es würde nur null für Knoten mit id=1, sonst jedes Kind hat seine Eltern ID sein)
  2. Die zweite Frage ist, dass in der Objektklasse nach der Codierung, wie könnte ich ein gewünschtes Kind legen Sie die ID-Namen mit geben? Zum Beispiel möchte I ein Kind mit id=4C und name=S112C nach Knoten mit id=4?

Update: (nachdem beide Frage zu beantworten)

Lassen Sie uns gehen wir davon aus, dass ich ein neues Feld als Hierarchy im Step erstellen möchten, die Werte der Zeichenfolge nimmt erstellt/vom Benutzer gegeben

Step.Hierarchy = // some strings ; 

Es bedeutet, ich möchte es durch ParentId ersetzen. Der Grund dafür ist, dass, weil manchmal gibt es einige Situationen, die ich zwei leere Knoten/Komponenten einsetzen sollte (Es gibt keinen Namen und Id für sie, wie weiter unten) als Kind für einige Schritte

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

wo ein leerer Knoten wird Kind eines anderen sein. Dann werde ich Schwierigkeiten haben, PrentId Referenz für den zweiten Knoten (Kind zu dem obigen Knoten) zu geben.

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

diesem Grund habe ich wie Hierarchy ein virtuelles Feld erstellen möchten einen beliebigen Wert zuweisen und ParentId es statt Id zu beziehen. Dann hat jeder Schritt eine nicht null Referenz.

Wenn Sie eine Idee haben, die dankbar wäre !!

+0

@dbc Vielen Dank für deinen Kommentar und deine Antwort! In der zweiten Frage meine ich, einen Schritt in die Step-Hierarchie einzufügen, indem ich nach einer bestimmten Step-Klasse suche. – Royeh

Antwort

1

Wie kann ich sicherstellen, dass child.ParentId nach der Deserialisierung immer gleich parent.Id ist?

Der natürliche Ansatz zur Einstellung Step.ParentId nach Deserialisierung wäre dies in einem OnDeserialized Ereignis zu tun. Leider XmlSerializer does not support deserialization events. In Anbetracht dessen müssen Sie möglicherweise ein alternatives Design untersuchen.

Eine Möglichkeit ist, Ihr List<Step> mit einer benutzerdefinierten Sammlung zu ersetzen, die automatisch die ParentId Referenzaufrechterhält, wenn ein Kind mit einem Elternteil hinzugefügt wird, entlang der Linien von Maintaining xml hierarchy (ie parent-child) information in objects generated by XmlSerializer. Leider ist ObservableCollection für diesen Zweck nicht geeignet, weil the list of old items is not included in the notification event when it is cleared.Es ist jedoch ziemlich einfach, durch Unterklassen System.Collections.ObjectModel.Collection<T> unser eigenes zu erstellen.

So würde Ihr Objektmodell das folgende werden. Beachten Sie, dass ich einige Ihrer Eigenschaftsnamen geändert c# naming guidelines folgen:

[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); 
    } 
} 

Jetzt ParentId wird gesetzt, wenn ein Kind mit einem Elternteil hinzugefügt wird, sowohl nach deserialzation und in jeder Code-Anwendungen.

(Wenn Sie aus irgendeinem Grund nicht können Ihre List<Step> mit einem Collection<Step> ersetzen, könnten Sie ein Array-Proxy-Eigenschaft und die Einstellung der ParentId Werte in der Setter, entlang der Linien von XML deserialization with parent object reference betrachten Serialisierung. Aber ich denke, ein Design, das automatisch id setzt die Eltern in allen Situationen vorzuziehen.)

Wie kann ich ein Step an einen Baum von Step Objekte hinzufügen, indem ParentId Angabe?

Sie könnten rekursiv erstellen Linq Erweiterungen, die die Step Hierarchie durchqueren, entlang der Linien von 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; 
    } 
} 

Them ein Kind zu einem bestimmten Elternteil von ID hinzufügen möchten, würden Sie tun:

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

Prototyp fiddle.

aktualisieren

Wenn Sie irgendwie Probleme mit der Zugabe von extension methods-Step und Steps weil sie Klassen verschachtelt sind, können Sie TraverseSteps() und Add() als Objektmethoden könnte hinzufügen:

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

Danke für Ihre Antwort. Ich habe einen Fehler bezüglich der ersten Frage. 'Fehler CS0246: Der Name des Typs oder Namensbereichs 'Sammlung' wurde nicht gefunden. ' Darf ich Sie um Hilfe bitten? – Royeh

+0

['Collection '] (https://msdn.microsoft.com/en-us/library/ms132397%28v=vs.110%29.aspx) ist im Namespace 'System.Collections.ObjectModel' so, dass Sie brauchen 'using System.Collections.ObjectModel;' – dbc

+0

Sorry, ich hatte es aber es war ein Tippfehler drin. Übrigens habe ich für die zweite Frage diesen Fehler: 'Fehler CS1109: Erweiterungsmethode muss in einer statischen Klasse auf oberster Ebene definiert sein; StepExtensions ist eine geschachtelte Klasse. Ich habe es aus dem Hauptformular heraus, aber immer noch habe ich den Fehler – Royeh

Verwandte Themen