2

I'm currently working on a project where an application will take on XML files and display it into a treeview in C#. I'm using Visual Studio 10 to write this code.

I cannot limit number of times the attributes are displayed. I used a foreach loop to loop through each of the attributes it has and display it, but it's displaying the attributes once for each childnode it has under the node. How can I modify this code to only display the attributes once?

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

And here is an example of my 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>
Jeffrey Bosboom
  • 13,313
  • 16
  • 79
  • 92
Softmochi
  • 119
  • 1
  • 11

2 Answers2

1

You need to move the loop through attributes out of the loop through child nodes:

    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

If you want to filter out namespace attributes, you can add 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:");
    }
}

Then use it to skip unwanted XmlAttribute instances. You can also explicitly set the text of all nodes of type XmlElement to be name + attribute data, not just those elements with children, using OuterXml only for text nodes:

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

If you really want to filter out just the default namespace definitions but leave others, you could do:

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

Incidentally, I don't recommend doing this since the following actually mean the same thing, namely an element with local name DataConfiguration in the namespace http://somenamespace:

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

Your tree will display the namespace for the first element but not the second.

Update 2

To include the XmlDeclaration in the tree, change the top level loop to be:

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

Update 3

Put the loop to include the XmlDeclaration loop in 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);
        }
    }
dbc
  • 104,963
  • 20
  • 228
  • 340
  • I see, now that will fix the problem, thank you! Also I'm getting the xmlns= namespace behind all my childnodes, how should i fix this problem? And it looks like they only appear when I use OutXml to display the remaining informations. One more question, is there a length for a node? Because my comment nodes get cut off at the end of the node. – Softmochi Mar 13 '15 at 16:04
  • @StudentPlusOne - answer updated. There is no limit on the length of the text of a node. Make sure your tree is [`Scrollable`](https://msdn.microsoft.com/en-us/library/system.windows.forms.treeview.scrollable.aspx). – dbc Mar 13 '15 at 16:56
  • Thank you for answering, your updated code works and it will remove all attributes that contain "xmln=", but this will also eliminate part of the attribute from , is there a different way of doing it? Also using the extension method was a pretty smart thing too, I learned a lot! Thanks – Softmochi Mar 13 '15 at 20:59
  • @StudentPlusOne - is `"xmln="` a typo in your question, or do you really have an attribute named `xmln`? The standard namespace attribute prefix is `xmlns` so I reckoned it was a mistake. – dbc Mar 13 '15 at 21:04
  • the code looks like this, the red indicate the repeat of "xmln=" attributes http://postimg.org/image/apmg0fpl7/ – Softmochi Mar 13 '15 at 21:21
  • @StudentPlusOne - Does the XML in your question have a typo? It starts out as ` – dbc Mar 13 '15 at 21:30
  • yes sorry that was a typo, it starts as xmlns, thank you for correcting me – Softmochi Mar 13 '15 at 21:33
  • @StudentPlusOne - [`dom.ChildNodes`](https://msdn.microsoft.com/en-us/library/system.xml.xmlnode.childnodes%28v=vs.110%29.aspx) will contain the `XmlDeclaration` as well as the root element. – dbc Mar 13 '15 at 22:13
  • I'm having another problem on how to export treeview to xml, here is a link to my other post, can you help me please http://stackoverflow.com/questions/29087531/treeview-to-xml-with-proper-indentation-c-sharp – Softmochi Mar 16 '15 at 21:46
  • Also just need a confirmation, where do I put the Xml Declaration loop in? @dbc – Softmochi Mar 17 '15 at 00:37
  • couple of my treenode got cut off due to the information on a single node is too long, is there way to fix that, the node that usually got cut off is a comment section from the XML file. Also my treeview is scrollable @dbc – Softmochi Mar 24 '15 at 22:47
-1

Try simple recursive algorithm

        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;
        }
jdweng
  • 33,250
  • 2
  • 15
  • 20