简体   繁体   中英

XML Parsing. Hide Parent node

So I have an xml file parsed to a TreeView list, currently I have about four levels of nodes when I only want three. I want to hide/remove the parent node or the top node since it's not much useful. I could do that by editing the XML file itself, but I'm not allowed to. Here's the code:

private void Form1_Load_1(object sender, EventArgs e)
    {
        // Initialize the controls and the form.
        textBox2.Text = Application.StartupPath + "\\Continental.vsysvar";
    }

    private void button6_Click(object sender, EventArgs e)
    {
        treeView1.BeginUpdate();
        try
        {
            // SECTION 1. Create a DOM Document and load the XML data into it.
            XmlDocument dom = new XmlDocument();
            dom.Load(textBox2.Text);
            // SECTION 2. Initialize the TreeView control.
            try
            {
                // SECTION 2. Initialize the TreeView control.
                //treeView1.Nodes.Clear();
                XmlTreeViewHelper.AddOrMergeNodes(treeView1.Nodes, dom.DocumentElement.ChildNodes, GetTreeNodeName, GetTreeNodeText, FilterNode);
                // SECTION 3. Populate the TreeView with the DOM nodes.
                treeView1.ExpandAll();
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.Message);
            }
            finally
            {
                treeView1.EndUpdate();
            }
        }
        catch (Exception ex)
        {
            MessageBox.Show(ex.Message);
        }
    }

    static string GetTreeNodeName(XmlNode inXmlNode)
    {
        string text = GetAttributeText(inXmlNode, "name");
        if (string.IsNullOrEmpty(text))
            text = inXmlNode.Name;
        return text;
    }

    static string GetTreeNodeText(XmlNode inXmlNode)
    {
        string text = GetAttributeText(inXmlNode, "name");
        if (string.IsNullOrEmpty(text))
        {
            if (inXmlNode.HasChildNodes)
            {
                text = inXmlNode.Name;
            }
            else
            {
                text = (inXmlNode.OuterXml).Trim();
            }
        }
        return text;
    }

    string filter = "_start"; // Reload when this changes.

    bool FilterNode(XmlNode inXmlNode)
    {
        return FilterNode(inXmlNode, filter);
    }

    bool FilterNode(XmlNode inXmlNode, string nodeNameFilter)
    {
        if (inXmlNode.Name == "namespace" && inXmlNode.ChildNodes.Count == 0 && string.IsNullOrEmpty(GetAttributeText(inXmlNode, "name")))
            return false;
        if (!string.IsNullOrEmpty(nodeNameFilter))
        {
            string text = GetTreeNodeText(inXmlNode);
            if (text.Contains(nodeNameFilter))
                return false;
        }
        return true;
    }
    static string GetAttributeText(XmlNode inXmlNode, string name)
    {   
        XmlAttribute attr = (inXmlNode.Attributes == null ? null : inXmlNode.Attributes[name]);
        return attr == null ? null : attr.Value;
    }

There's a class named XmlTreeViewHelper, but it's quite long so I decided not to include it here (and it's main purpose's to filter nodes with a particular set of string). Let me know if I also need to include it here.

Anyway, when I click the button, the result would be like this:

namespace
|---Category A
|-------Object A1
|-------Object A2
|---Category B
|-------Object B1
|-------Object B2

How do I hide the namespace? I'm not allowed to delete it on the xml file itself. The list should only be showing the Categories and the Objects. FYI, the namespace is shown in the list as namespace. Below is the sample of the XML file.

<?xml version="1.0" encoding="utf-8"?>
<systemvariables version="4">
  <namespace name="" comment="">
    <namespace name="_01_Test_Preparation" comment="">
      <variable anlyzLocal="2" readOnly="false" valueSequence="false" unit="" name="_01_02_Shipping_Status_Check" comment="" bitcount="32" isSigned="true" encoding="65001" type="int" startValue="0" minValue="0" minValuePhys="0" maxValue="4" maxValuePhys="4" />
      <variable anlyzLocal="2" readOnly="false" valueSequence="false" unit="" name="_01_02_Shipping_Status_Check_start" comment="" bitcount="32" isSigned="true" encoding="65001" type="int" startValue="0" minValue="0" minValuePhys="0" maxValue="4" maxValuePhys="4" />
    </namespace>
    <namespace name="_02_Communication" comment="">
      <variable anlyzLocal="2" readOnly="false" valueSequence="false" unit="" name="_02_04_VCAN_StartLoad" comment="" bitcount="32" isSigned="true" encoding="65001" type="int" startValue="0" minValue="0" minValuePhys="0" maxValue="4" maxValuePhys="4" />
      <variable anlyzLocal="2" readOnly="false" valueSequence="false" unit="" name="_02_08_XCP_Restbus_RAM_Monitor" comment="" bitcount="32" isSigned="true" encoding="65001" type="int" startValue="0" minValue="0" minValuePhys="0" maxValue="4" maxValuePhys="4" />
    </namespace>
  </namespace>
</systemvariables>

I'm thinking that if a namespace's attribute name is empty, it won't be included in the list. or at least it won't be shown. Is this possible? Most of the examples I've found on the Web are concerned with removing parent nodes without child nodes, but in my case, this parent node has child nodes.

The easiest thing to do would be to load the ChildNodes of the ChildNodes of the DocumentElement , rather than the ChildNodes of the DocumentElement .

First, change XmlTreeViewHelper.AddOrMergeNodes() to work with any IEnumerable<XmlNode> :

public static class XmlNodeHelper
{
    public static IEnumerable<XmlNode> ChildNodes(IEnumerable<XmlNode> xmlNodeList)
    {
        if (xmlNodeList == null)
            yield break;
        foreach (XmlNode node in xmlNodeList)
            foreach (XmlNode childNode in node.ChildNodes)
                yield return childNode;
    }

    public static IEnumerable<TNode> OfType<TNode>(XmlNodeList xmlNodeList) where TNode : XmlNode
    {
        // Convert XmlNodeList which implements non-generic IEnumerable to IEnumerable<XmlNode> by downcasting the nodes
        if (xmlNodeList == null)
            yield break;
        foreach (XmlNode xmlNode in xmlNodeList)
            if (xmlNode is TNode)
                yield return (TNode)xmlNode;
    }
}

public static class XmlTreeViewHelper
{
    public static void AddOrMergeNodes(TreeNodeCollection treeNodeCollection, IEnumerable<XmlNode> xmlNodeList, GetString<XmlNode> getNodeName, GetString<XmlNode> getNodeText, Predicate<XmlNode> filter)
    {
        Dictionary<string, List<TreeNode>> dict = ToNodeDictionary(treeNodeCollection);
        int index = 0;
        foreach (XmlNode inXmlNode in xmlNodeList)
        {
            AddOrMergeNode(treeNodeCollection, inXmlNode, ref index, getNodeName, getNodeText, filter, dict);
        }

        foreach (List<TreeNode> list in dict.Values)
            foreach (TreeNode leftover in list)
            {
                treeNodeCollection.Remove(leftover);
            }
    }

    static bool IsNodeAtIndex(TreeNodeCollection nodes, TreeNode node, int index)
    {
        // Avoid n-squared nodes.IndexOf(node).
        if (index < 0 || index >= nodes.Count)
            return false;
        return nodes[index] == node;
    }

    static void AddOrMergeNode(TreeNodeCollection treeNodeCollection, XmlNode inXmlNode, ref int index, GetString<XmlNode> getNodeName, GetString<XmlNode> getNodeText, Predicate<XmlNode> filter, Dictionary<string, List<TreeNode>> dict)
    {
        if (filter != null && !filter(inXmlNode))
            return;

        string treeName = getNodeName(inXmlNode);
        string treeText = (getNodeText == null ? treeName : getNodeText(inXmlNode));

        bool added = false;

        TreeNode treeNode;
        if (!DictionaryExtensions.TryRemoveFirst(dict, treeName, out treeNode))
        {
            treeNode = new TreeNode();
            treeNode.Name = treeName;
            treeNode.Text = treeText;
            added = true;
            treeNodeCollection.Insert(index, treeNode);
        }
        else
        {
            if (!IsNodeAtIndex(treeNodeCollection, treeNode, index))
            {
                treeNodeCollection.Remove(treeNode);
                treeNodeCollection.Insert(index, treeNode);
            }
        }

        index++;

        if (treeNode.Text != treeText)
            treeNode.Text = treeText;

        if (inXmlNode.HasChildNodes)
            AddOrMergeNodes(treeNode.Nodes, XmlNodeHelper.OfType<XmlNode>(inXmlNode.ChildNodes), getNodeName, getNodeText, filter);
        else
            treeNode.Nodes.Clear();

        if (added)
            treeNode.ExpandAll();
    }

    /// <summary>
    /// Returns a dictionary of tree nodes by node name.
    /// </summary>
    /// <param name="nodes"></param>
    /// <returns></returns>
    static Dictionary<string, List<TreeNode>> ToNodeDictionary(TreeNodeCollection nodes)
    {
        Dictionary<string, List<TreeNode>> dict = new Dictionary<string, List<TreeNode>>();
        foreach (TreeNode node in nodes)
            DictionaryExtensions.Add(dict, node.Name, node);
        return dict;
    }
}

Then, simply load the nodes one level lower in the document:

            XmlTreeViewHelper.AddOrMergeNodes(treeView1.Nodes, XmlNodeHelper.ChildNodes(XmlNodeHelper.OfType<XmlNode>(dom.DocumentElement.ChildNodes)), GetTreeNodeName, GetTreeNodeText, FilterNode);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM