2010-09-07 97 views
4

我有一個「扁平」的XML菜單,我需要構造。XML樹的生成

當前XML樹:

<root> 
    <nodes> 
     <node> 
      <id>5</id> 
      <parent>1</parent> 
     </node> 
     <node> 
      <id>8</id> 
      <parent>5</parent> 
     </node> 
     <node> 
      <id>14</id> 
      <parent>8</parent> 
     </node> 
     <node> 
      <id>26</id> 
      <parent>1</parent> 
     </node> 
    </nodes>  
</root> 

此XML樹需要被reodered有ID之間的正確關係:S和PARENTID:S

<root> 
    <nodes> 
     <node> 
      <id>5</id> 
      <parent>1</parent> 
      <node> 
       <id>8</id> 
       <parent>5</parent> 
       <node> 
        <id>14</id> 
        <parent>8</parent> 
       </node> 
      </node> 
     </node>    
     <node> 
      <id>26</id> 
      <parent>1</parent> 
     </node> 
    </nodes>  
</root> 

㈣得到了下面的代碼,以盡力去完成這樣的:

public XmlDocument SortXmlNodeTree(XmlDocument udoc) 
{ 
    XmlDocument sortedDoc = new XmlDocument(); 
    sortedDoc.LoadXml(xmlStartString); 

    //select top nodes 
    //top node -> find all siblings. For each sibling add sibling.siblings. ->loop    
    XmlNode nodes = udoc.DocumentElement.LastChild; 
    foreach(XmlNode n in nodes) 
    { 
     //get top nodes and check if they are folders 
     if (n["parent"].InnerText.Equals("1") && n["type"].InnerText.Equals("2")) 
     { 
      XmlNode newNode = sortedDoc.ImportNode(n, true);       
      GetNodeSiblings(ref nodes, newNode, ref sortedDoc);  
      SortedDoc.DocumentElement.FirstChild.AppendChild(newNode);      
     } 
    } 
    return sortedDoc; 
} 

public XmlNode GetNodeSiblings(ref XmlNode nodes, XmlNode currentNode, ref XmlDocument tree) 
{ 
    if (!nodes.HasChildNodes) 
    { 
     return null; 
    } 

    foreach (XmlNode n in nodes) 
    { 
     // if we have a folder and parent is currentNode, go deeper 
     if (n["type"].InnerText.Equals("2") && n["parent"].InnerText.Equals(currentNode["id"].InnerText)) 
     { 
      XmlNode newNode = tree.ImportNode(n, true);      
      GetNodeSiblings(ref nodes, newNode, ref tree); 
      currentNode.AppendChild(newNode); 
     } 
     // if we have a product that has currentNode as parent, add it. 
     else if (!n["type"].InnerText.Equals("2") && n["parent"].InnerText.Equals(currentNode["id"].InnerText)) 
     { 
      XmlNode newNode = tree.ImportNode(n, true); 
      nodes.RemoveChild(n); 
      currentNode.AppendChild(newNode); 
     } 
    } 
    return null; 
} 

正如你可以看到我的節點也包含「類型」和「名稱」。類型用於確定節點是「文件夾」還是「產品」。

我的問題是,這dosn't返回正確的XML。如果我在最後一節刪除nodes.RemoveChild(n),那麼它的工作效果很好,但是我想刪除我知道沒有任何孩子的孩子(產品,type = 1)。

如果此代碼已運行。我只有幾個節點。

回答

2

此代碼完成這項工作。希望這是很清楚

public class Node 
{ 
    public Node() 
    { 
     Children = new List<Node>(); 
    } 

    public int Id; 

    public int ParentId; 

    public List<Node> Children; 

    public Node Parent; 

    public static Node Deserialize(XmlElement xNode) 
    { 
     Node n = new Node(); 
     XmlElement xId = xNode.SelectSingleNode("id") as XmlElement; 
     n.Id = int.Parse(xId.InnerText); 
     XmlElement xParent = xNode.SelectSingleNode("parent") as XmlElement; 
     n.ParentId = int.Parse(xParent.InnerText); 
     return n; 
    } 

    public void AddChild(Node child) 
    { 
     Children.Add(child); 
     child.Parent = this; 
    } 

    public void Serialize(XmlElement xParent) 
    { 
     XmlElement xNode = xParent.OwnerDocument.CreateElement("node"); 
     XmlElement xId = xParent.OwnerDocument.CreateElement("id"); 
     xId.InnerText = Id.ToString(); 
     xNode.AppendChild(xId); 
     XmlElement xParentId = xParent.OwnerDocument.CreateElement("parent"); 
     xParentId.InnerText = ParentId.ToString(); 
     xNode.AppendChild(xParentId); 
     foreach (Node child in Children) 
      child.Serialize(xNode); 
     xParent.AppendChild(xNode); 
    } 
} 

public static XmlDocument DeserializeAndReserialize(XmlDocument flatDoc) 
{ 
    Dictionary<int, Node> nodes = new Dictionary<int, Node>(); 
    foreach (XmlElement x in flatDoc.SelectNodes("//node")) 
    { 
     Node n = Node.Deserialize(x); 
     nodes[n.Id] = n; 
    } 

    // at the end, retrieve parents for each node 
    foreach (Node n in nodes.Values) 
    { 
     Node parent; 
     if (nodes.TryGetValue(n.ParentId, out parent)) 
     { 
      parent.AddChild(n); 
     } 
    } 

    XmlDocument orderedDoc = new XmlDocument(); 
    XmlElement root = orderedDoc.CreateElement("root"); 
    orderedDoc.AppendChild(root); 
    XmlElement xnodes = orderedDoc.CreateElement("nodes"); 
    foreach (Node n in nodes.Values) 
    { 
     if (n.Parent == null) 
      n.Serialize(xnodes); 
    } 
    root.AppendChild(xnodes); 
    return orderedDoc; 
} 
+0

做到了!謝謝,很高興看到親們如何處理這個問題! – Marthin 2010-09-07 11:18:28

+0

我想快速使用我熟悉的代碼。有更多的時間,我會嘗試玩System.Xml.Linq。用這個程序集處理Xml要容易得多 – PierrOz 2010-09-07 11:42:24

4

我會採取不同的方法解決問題。您現在正嘗試通過移動相當複雜的節點來修改現有文檔。我會解析原始文檔,將其存儲在某個數據結構中,然後再將其寫入另一個位置。

你的數據結構將是這個樣子:

public class Node 
{ 
    public SomeClass NodeData { get ; set; } 
    public List<Node> Children { get; } 
} 

其中SomeClass是保存的相關數據爲單節點類型化的對象。然後你的代碼應該看起來像這樣:

Node rootNode = ParseXml(...); 
WriteStructuredXml(rootNode); 

這兩種方法都不難寫。這樣你可以將問題分成兩個更小,更簡單的問題。

+0

你說得對,應該按照你的建議來處理。謝謝你的建議! – Marthin 2010-09-07 11:20:01

0

下面是一些代碼,獲取所有節點名稱爲「節點」:

public static IEnumerable<XmlNode> GetNodes(XmlDocument xdoc) 
    { 
     var nodes = new List<XmlNode>(); 

     Queue<XmlNode> toProcess = new Queue<XmlNode>(); 

     if (xdoc != null) 
     { 
      foreach (var node in GetChildElements(xdoc)) 
      { 
       toProcess.Enqueue(node); 
      } 
     } 

     do 
     { 
      //get a node to process 
      var node = toProcess.Dequeue(); 

      // add node to found list if name matches 
      if (node.Name == "node") 
      { 
       nodes.Add(node); 
      } 

      // get the node's children 
      var children = GetChildElements(node); 

      // add children to queue. 
      foreach (var n in children) 
      { 
       toProcess.Enqueue(n); 
      } 

      // continue while queue contains items. 
     } while (toProcess.Count > 0); 


     return nodes; 
    } 

    private static IEnumerable<XmlNode> GetChildElements(XmlNode node) 
    { 
     if (node == null || node.ChildNodes == null) return new List<XmlNode>(); 

     return node.ChildNodes.Cast<XmlNode>().Where(n=>n.NodeType == XmlNodeType.Element); 
    } 

然後,你需要移動節點基於父子相對圍繞」船舶。見@ PierrOz的答案。