2010-09-07 71 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的答案。