2015-03-11 4 views
2

В настоящее время я работаю над проектом, в котором приложение будет использовать XML-файлы и отображать их в виде дерева в C#. Я использую Visual Studio 10 для написания этого кода.Рекурсия, анализ XML-файла с атрибутами в treeview C#

Я не могу ограничить количество показов атрибутов. Я использовал цикл foreach для прокрутки каждого из атрибутов, которые он имеет, и отображает его, но он отображает атрибуты один раз для каждого дочернего узла, который он имеет под узлом. Как я могу изменить этот код, чтобы отображать только атрибуты один раз?

using System; 
using System.Collections.Generic; 
using System.ComponentModel; 
using System.Data; 
using System.Drawing; 
using System.Linq; 
using System.Text; 
using System.Windows.Forms; 
using System.Xml; 

namespace xmlToTreeview 
{ 
    public partial class Form1 : Form 
    { 
     string samplePath = Application.StartupPath + @"\\sample.xml"; 
     public Form1() 
     { 
      InitializeComponent(); 
      DisplayTreeView(samplePath); 
     } 

     private void DisplayTreeView(string pathname) 
     { 
      try 
      { 
       // SECTION 1. Create a DOM Document and load the XML data into it. 
       XmlDocument dom = new XmlDocument(); 
       dom.Load(pathname); 


       // SECTION 2. Initialize the TreeView control. 
       treeView1.Nodes.Clear(); 
       treeView1.Nodes.Add(new TreeNode(dom.DocumentElement.Name)); 
       TreeNode tNode = new TreeNode(); 
       tNode = treeView1.Nodes[0]; 

       // SECTION 3. Populate the TreeView with the DOM nodes. 
       AddNode(dom.DocumentElement, tNode); 

      } 
      catch (XmlException xmlEx) 
      { 
       MessageBox.Show(xmlEx.Message); 
      } 
      catch (Exception ex) 
      { 
       MessageBox.Show(ex.Message); 
      } 
     } 
     private void AddNode(XmlNode inXmlNode, TreeNode inTreeNode) 
     { 
      XmlNode xNode; 
      TreeNode tNode; 
      XmlNodeList nodeList; 
      int i; 

      // Loop through the XML nodes until the leaf is reached. 
      // Add the nodes to the TreeView during the looping process. 

      if (inXmlNode.HasChildNodes) 
      { 
       nodeList = inXmlNode.ChildNodes; 

       for (i = 0; i <= nodeList.Count - 1; i++) 
       { 
        xNode = inXmlNode.ChildNodes[i]; 
        inTreeNode.Nodes.Add(new TreeNode(xNode.Name)); 
        tNode = inTreeNode.Nodes[i]; 

        //Check if the XmlNode has attributes 
        if (inXmlNode.Attributes.Count != 0) 
        { 
         foreach (XmlAttribute att in inXmlNode.Attributes) 
         { 
          inTreeNode.Text = inTreeNode.Text + " " + att.Name + ": " + att.Value; 
         } 
        } 
        AddNode(xNode, tNode); 
       } 
      } 
      else 
      { 
       // Here you need to pull the data from the XmlNode based on the 
       // type of node, whether attribute values are required, and so forth. 
       inTreeNode.Text = (inXmlNode.OuterXml).Trim(); 
      } 
      treeView1.ExpandAll(); 
     } 
    } 
} 

А вот пример моего XML

<?xml version="1.0" encoding="utf-8"?> 
<DataConfiguration xmln="abcefg12345" xmlns:xsi="12345abcefg" xsi:schemaLocation="12345abcefg12345abcefg"> 
    <Hosts> 
    <Sites> 
     <Site Name="ss"> 
      <Host Id="aa"> 
       <Address Host="www.www.com"> </Address> 
      </Host> 
      <Host Id="ee"> 
       <Address Host="www.www.com"> </Address> 
      </Host> 
      <Host Id="dd"> 
       <Address Host="www.www.com"> </Address> 
      </Host> 
      <Host Id="pp"> 
       <Address Scheme="ppp" Host="www.www.com" Path="www.www.com"/> 
       <Address Scheme="ppp" Host="www.www.com" Path="www.www.com/"/> 
      </Host> 
      <Host Id="ss"> 
       <Address Scheme="ppp" Host="www.www.com" Path="www.www.com"/> 
       <Address Scheme="ppp" Host="www.www.com" Path="www.www.com"/> 
      </Host> 
      <Host Id="561"> 
       <Address Host="www.www.com"> </Address> 
      </Host> 
     </Site> 
     <Site Name="hihi"> 
      <Host Id="cc"> 
       <Address Host="www.www.com"> </Address> 
      </Host> 
      <Host Id="sdD"> 
       <Address Host="www.www.com"> </Address> 
      </Host> 
      <Host Id="8uj"> 
       <Address Scheme="ppp" Host="www.www.com" Path="www.www.com"/> 
       <Address Scheme="ppp" Host="www.www.com" Path="www.www.com"/> 

      </Host> 
      <Host Id="222"> 
       <Address Scheme="ppp" Host="www.www.com" Path="www.www.com"/> 
       <Address Scheme="ppp" Host="www.www.com" Path="www.www.com"/>    
      </Host> 
      <Host Id="hhh"> 
       <Address Scheme="ppp" Host="www.www.com" Path="www.www.com"/> 
      </Host> 
      <Host Id="hhh"> 
       <Address Scheme="ppp" Host="www.www.com" Path="www.www.com"/> 
      </Host>    
     </Site>  
    </Sites> 
      <Host Id="hhh"> 
       <Address Scheme="ppp" Host="www.www.com" Path="www.www.com"/> 
      </Host> 
      <Host Id="hhh"> 
       <Address Scheme="ppp" Host="www.www.com" Path="www.www.com"/> 
      </Host> 
      <Host Id="hhh"> 
       <Address Scheme="ppp" Host="www.www.com" Path="www.www.com"/> 
      </Host>   
      <Host Id="hhh"> 
       <Address Scheme="ppp" Host="www.www.com" Path="www.www.com"/> 
      </Host> 

</Hosts> 
<DataPools> 
    <DataPool Id="sss" default="sure"> 
     <DataGroup Id="sss" Parent="aaa" UserCanSelectHost="sure" > 
      <HostId Parent="hhhh">I'm breaking here</HostId> 
      <DataSources> 
       <empty/> 
      </DataSources> 
     </DataGroup> 
     <DataGroup Id="ccc" UserCanSelectHost="whynot" > 
      <HostId>God I'm breaking here again, i hope you can fix me</HostId> 
      <DataSources> 
       <empty/> 
      </DataSources> 
     </DataGroup> 
     <DataGroup Id="sss" UserCanSelectHost="yessure" > 
      <HostId>cry face</HostId> 
       <webfg displaygroup="sss" provider="sss" id="ccc" principal="ccc" nioarc="ccc" nap="ccc" group="ccc"> 
       </webfg> 

       <nhood port="1234"/> 
      <ServerNames> 
       <!-- insert comment --> 
       <!-- insert comment --> 
       <!-- insert comment --> 
       <ServerName>myname</ServerName> 
       <ServerName>yourname</ServerName> 
      </ServerNames> 
      <!-- insert comment --> 
      <Implementations> 
       <Implementation> 
       <Name>yourname</Name> 
       <Type>typeme</Type> 
       <Assembly>visionme</Assembly> 
       <Path>ohno</Path> 
       </Implementation> 
      </Implementations>--> 
       <cfgman port="ccc" />    
       <webservice provider="ccc" /> 
       <webservice provider="ccc" /> 
       <webservice provider="ccc" /> 
        <parameters> 
         <useeventpush value="ccc"/> 
        </parameters> 
       <webservice provider="ccc" /> 
         <pollingFrequency value="1000"/> 
     </DataGroup> 
    </DataPool> 
    <DataGroup Id="ccc " UserCanSelectHost="ccc" > 
     <DataGroup Id="ccc " UserCanSelectHost="ccc" > 
      <HostId>idk</HostId> 
      <DataSources> 
       <empty/> 
      </DataSources> 
     </DataGroup> 
     <DataGroup Id="ccc " UserCanSelectHost="ccc" > 
      <HostId>idk</HostId> 
      <DataSources> 
       <empty/> 
      </DataSources> 
     </DataGroup> 
     <DataGroup Id="default" UserCanSelectHost="true" > 
      <HostId>idk</HostId> 
     </DataGroup> 
    </DataGroup>  
</DataPools>  
</DataConfiguration> 

ответ

1

Вы должны переместить петлю через атрибуты из петли через дочерние узлы:

private void AddNode(XmlNode inXmlNode, TreeNode inTreeNode) 
    { 
     // Loop through the XML nodes until the leaf is reached. 
     // Add the nodes to the TreeView during the looping process. 

     if (inXmlNode.HasChildNodes) 
     { 
      //Check if the XmlNode has attributes 
      foreach (XmlAttribute att in inXmlNode.Attributes) 
      { 
       inTreeNode.Text = inTreeNode.Text + " " + att.Name + ": " + att.Value; 
      } 

      var nodeList = inXmlNode.ChildNodes; 
      for (int i = 0; i < nodeList.Count; i++) 
      { 
       var xNode = inXmlNode.ChildNodes[i]; 
       var tNode = inTreeNode.Nodes[inTreeNode.Nodes.Add(new TreeNode(xNode.Name))]; 
       AddNode(xNode, tNode); 
      } 
     } 
     else 
     { 
      // Here you need to pull the data from the XmlNode based on the 
      // type of node, whether attribute values are required, and so forth. 
      inTreeNode.Text = (inXmlNode.OuterXml).Trim(); 
     } 
     treeView1.ExpandAll(); 
    } 

Update

Если вы хотите отфильтровать атрибуты пространства имен, Вы можете добавить extension methods:

public static class XmlNodeExtensions 
{ 
    public static bool IsDefaultNamespaceDeclaration(this XmlAttribute attr) 
    { 
     if (attr == null) 
      return false; 
     if (attr.NamespaceURI != "http://www.w3.org/2000/xmlns/") 
      return false; 
     return attr.Name == "xmlns"; 
    } 

    public static bool IsNamespaceDeclaration(this XmlAttribute attr) 
    { 
     if (attr == null) 
      return false; 
     if (attr.NamespaceURI != "http://www.w3.org/2000/xmlns/") 
      return false; 
     return attr.Name == "xmlns" || attr.Name.StartsWith("xmlns:"); 
    } 
} 

Затем используйте его, чтобы пропустить ненужные XmlAttribute экземпляров. Вы также можете явно задать текст всех узлов типа XmlElement быть имя + атрибутов данных, а не только те элементы, с детьми, используя OuterXml только для текстовых узлов:

private void AddNode(XmlNode inXmlNode, TreeNode inTreeNode) 
    { 
     if (inXmlNode is XmlElement) 
     { 
      // An element. Display element name + attribute names & values. 
      foreach (var att in inXmlNode.Attributes.Cast<XmlAttribute>().Where(a => !a.IsNamespaceDeclaration())) 
      { 
       inTreeNode.Text = inTreeNode.Text + " " + att.Name + ": " + att.Value; 
      } 
      // Add children 
      foreach (XmlNode xNode in inXmlNode.ChildNodes) 
      { 
       var tNode = inTreeNode.Nodes[inTreeNode.Nodes.Add(new TreeNode(xNode.Name))]; 
       AddNode(xNode, tNode); 
      } 
     } 
     else 
     { 
      // Not an element. Character data, comment, etc. Display all text. 
      inTreeNode.Text = (inXmlNode.OuterXml).Trim(); 
     } 
     treeView1.ExpandAll(); 
    } 

Если вы действительно хотите, чтобы отфильтровать только определения пространства имен по умолчанию, но оставить другим, вы можете сделать:

  // An element. Display element name + attribute names & values. 
      foreach (var att in inXmlNode.Attributes.Cast<XmlAttribute>().Where(a => !a.IsDefaultNamespaceDeclaration())) 
      { 
       inTreeNode.Text = inTreeNode.Text + " " + att.Name + ": " + att.Value; 
      } 

Кстати, я не рекомендую делать это, так как следующий на самом деле означает то же самое, а именно элемент с именем локального DataConfiguration в пространстве имен http://somenamespace:

<ss:DataConfiguration xmlns:ss="http://somenamespace"/> 
<DataConfiguration xmlns="http://somenamespace"/> 

Ваше дерево будет отображать пространство имен для первого элемента, но не второе.

Обновление 2

Чтобы включить XmlDeclaration в дереве, изменить верхнюю петлю уровня, чтобы быть:

 treeView1.Nodes.Clear(); 
     foreach (XmlNode xNode in dom.ChildNodes) 
     { 
      var tNode = treeView1.Nodes[treeView1.Nodes.Add(new TreeNode(xNode.Name))]; 
      AddNode(xNode, tNode); 
     } 

Обновление 3

Поместите петлю, чтобы включить XmlDeclaration петля в DisplayTreeView:

private void DisplayTreeView(string pathname) 
    { 
     try 
     { 
      // SECTION 1. Create a DOM Document and load the XML data into it. 
      XmlDocument dom = new XmlDocument(); 
      dom.Load(pathname); 

      // SECTION 2. Initialize the TreeView control. 
      treeView1.Nodes.Clear(); 

      // SECTION 3. Populate the TreeView with the XML nodes. 
      foreach (XmlNode xNode in dom.ChildNodes) 
      { 
       var tNode = treeView1.Nodes[treeView1.Nodes.Add(new TreeNode(xNode.Name))]; 
       AddNode(xNode, tNode); 
      } 
     } 
     catch (XmlException xmlEx) 
     { 
      MessageBox.Show(xmlEx.Message); 
     } 
     catch (Exception ex) 
     { 
      MessageBox.Show(ex.Message); 
     } 
    } 
+0

Я вижу, теперь будет решить эту проблему, спасибо! Также я получаю пространство имен xmlns = позади всех моих дочерних узлов, как мне исправить эту проблему? И похоже, что они появляются только при использовании OutXml для отображения оставшейся информации. Еще один вопрос: есть ли длина для узла? Потому что мои узлы комментариев обрезаются в конце узла. – StudentPlusOne

+0

@StudentPlusOne - ответ обновлен. Нет ограничений на длину текста узла. Убедитесь, что ваше дерево ['Scrollable'] (https://msdn.microsoft.com/en-us/library/system.windows.forms.treeview.scrollable.aspx). – dbc

+0

Благодарим вас за ответ, ваш обновленный код работает, и он удалит все атрибуты, содержащие «xmln =», но это также устранит часть атрибута из , есть ли другой способ сделать это? Также использование метода расширения было довольно умным, я многому научился! Спасибо – StudentPlusOne

-1

Попробуйте простой рекурсивный алгоритм

 static void Main(string[] args) 
     { 
      string input = 
       "<a>" + 
        "<b>" + 
         "<c>val1</c>" + 
         "<d>val2</d>" + 
        "</b>" + 
        "<e>val3</e>" + 
        "<f>val4</f>" + 
       "</a>"; 

      XmlDocument doc = new XmlDocument(); 
      doc.LoadXml(input); 
      RecursiveRead(doc.DocumentElement); 

     } 
     static string RecursiveRead(XmlNode node) 
     { 
      List<string> children = new List<string>(); 
      bool done = false; 

      if (node.HasChildNodes) 
      { 
       foreach (XmlNode child in node) 
       { 
        children.Add(RecursiveRead(child)); 
       } 
       Console.WriteLine("table : {0}; children : {1}", node.Name, string.Join(",", children.ToArray())); 
       //string cmd = "INSERT INTO " + second + " (" + first + ") VALUES ('" + xml.Value + "')"; 
       //SqlCommand command = new SqlCommand(cmd, conn); 
       //command.ExecuteNonQuery(); 
      } 
      else 
      { 
       return node.Value; 
      } 
      return node.Name; 
     }