0

In my previous question: link,

I learnt how to use extension method called "DescendantsUntil()" to find the topmost elements (Parent) and their topmost descendants (Children).

Let's suppose now to have more descendants (Parent, Children, Nephews and so on..):

<?xml version="1.0" encoding="utf-8"?>
<Document>
    <Interface>
        <Sections xmlns="http://www.siemens.com/automation/Openness/SW/Interface/v4">
            <Section Name="Static">
                <Member Name="3bool1" Datatype="&quot;3bool&quot;" Remanence="NonRetain" Accessibility="Public">
                    <AttributeList>
                        <BooleanAttribute Name="ExternalAccessible" SystemDefined="true">true</BooleanAttribute>
                        <BooleanAttribute Name="ExternalVisible" SystemDefined="true">true</BooleanAttribute>
                        <BooleanAttribute Name="ExternalWritable" SystemDefined="true">true</BooleanAttribute>
                        <BooleanAttribute Name="SetPoint" SystemDefined="true">false</BooleanAttribute>
                    </AttributeList>
                    <Sections>
                        <Section Name="None">
                            <Member Name="bool1" Datatype="Bool" />
                            <Member Name="bool2" Datatype="Bool" />
                            <Member Name="bool3" Datatype="Bool" />
                            <Member Name="3bool1" Datatype="&quot;3bool&quot;" Remanence="NonRetain" Accessibility="Public">
                                <AttributeList>
                                    <BooleanAttribute Name="ExternalAccessible" SystemDefined="true">true</BooleanAttribute>
                                    <BooleanAttribute Name="ExternalVisible" SystemDefined="true">true</BooleanAttribute>
                                    <BooleanAttribute Name="ExternalWritable" SystemDefined="true">true</BooleanAttribute>
                                    <BooleanAttribute Name="SetPoint" SystemDefined="true">false</BooleanAttribute>
                                </AttributeList>
                                <Sections>
                                    <Section Name="None">
                                        <Member Name="bool1" Datatype="Bool" />
                                        <Member Name="bool2" Datatype="Bool" />
                                        <Member Name="bool3" Datatype="Bool" />
                                    </Section>
                                </Sections>
                            </Member>
                        </Section>
                    </Sections>
                </Member>
                <Member Name="int7" Datatype="Int" Remanence="NonRetain" Accessibility="Public">
                    <AttributeList>
                        <BooleanAttribute Name="ExternalAccessible" SystemDefined="true">true</BooleanAttribute>
                        <BooleanAttribute Name="ExternalVisible" SystemDefined="true">true</BooleanAttribute>
                        <BooleanAttribute Name="ExternalWritable" SystemDefined="true">true</BooleanAttribute>
                        <BooleanAttribute Name="SetPoint" SystemDefined="true">true</BooleanAttribute>
                    </AttributeList>
                </Member>
            </Section>
        </Sections>
    </Interface>
 </Document>

By using DescendantsUntil() extension method, I can easily filter parent and children

string path = ("C:\\Users\\");
XDocument doc = XDocument.Load(path + "File.xml");
XNamespace ns = "http://www.siemens.com/automation/Openness/SW/Interface/v4";
XName name = ns + "Member";

var memb = doc
    .Root.DescendantsUntil(e => e.Name == name)
    .Select(e => (Parent: e, Children: e.DescendantsUntil(c => c.Name == name).ToList()))
    //.Where(i => i.Children.Count > 0); // Uncomment to filter out <Member> elements with no child members.
    .ToList();
  

Now, how can I use DescendantsUntil() to extract Parent, Children, Nephews elements, and in general, how to extract all descendants as long as there's another nested one?

  • You have a tree structure and have to use tree algorithms to parse in pieces. You probably need a recursive algorithm. – jdweng Apr 13 '23 at 10:24

2 Answers2

0

Try code like this :

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.Xml.Linq;

namespace ConsoleApplication52
{
    class Program
    {
        const string FILENAME = @"c:\temp\test.xml";
        static void Main(string[] args)
        {
            XDocument doc = XDocument.Load(FILENAME);
            XElement xSections = doc.Descendants().Where(x => x.Name.LocalName == "Sections").FirstOrDefault();
            XNamespace ns = xSections.GetDefaultNamespace();
            Sections root = new Sections();
            root.ParseSections(xSections, ns);
        }
    }
    public class Sections
    {
        public List<Section> sections { get; set; }

        public void ParseSections(XElement xSections, XNamespace ns)
        {
            List<XElement> sections = xSections.Elements(ns + "Section").ToList();
            foreach (XElement section in sections)
            {
                if (this.sections == null) this.sections = new List<Section>();
                Section childSection = new Section();
                this.sections.Add(childSection);
                childSection.ParseSection(section, ns);
            }
        }
    }
    public class Section
    {
        public string name { get; set; }
        public List<Member> members { get; set; }

        public void ParseSection(XElement xSection, XNamespace ns)
        {
            this.name = (string)xSection.Attribute("Name");
            foreach (XElement xMember in xSection.Elements(ns + "Member"))
            {
                if (this.members == null) this.members = new List<Member>();
                Member member = new Member();
                this.members.Add(member);
                member.ParseMember(xMember, ns);

            }
        }
    }
    public class Member
    {
        public string name { get; set; }
        public string remanence { get; set; }
        public string accessibility { get; set; }
        public Dictionary<string, Boolean> attributes { get; set; }
        public Sections sections { get; set; }

        public void ParseMember(XElement member, XNamespace ns)
        {
            this.name = (string)member.Attribute("Name");
            this.remanence = (string)member.Attribute("Remanence");
            this.accessibility = (string)member.Attribute("Accessibility");
            XElement attributeList = member.Element(ns + "AttributeList");
            if (attributeList != null)
            {
                foreach (XElement attribute in attributeList.Descendants(ns + "BooleanAttribute"))
                {
                    if (attributes == null) attributes = new Dictionary<string, bool>();
                    string attributeName = (string)attribute.Attribute("Name");
                    Boolean attributeValue = (Boolean)attribute;
                    attributes.Add(attributeName, attributeValue);
                }
            }
            XElement xSections = member.Element(ns + "Sections");
            if (xSections != null)
            {
                Sections childSections = new Sections();
                this.sections = childSections;
                childSections.ParseSections(xSections, ns);
            }
            
        }
    }
}
jdweng
  • 33,250
  • 2
  • 15
  • 20
0

I found the solution by myself (maybe it might be useful for you).

If you want to use the extension method "DescendantsUntil" to find more nested levels, you just have to call it again inside the foreach statement, like the following working code:

var memb_L0 = doc
    .Root.DescendantsUntil(e => e.Name == name)
    .Select(e => (Parent: e, Children: e.DescendantsUntil(c => c.Name == name).ToList()))
    //.Where(i => i.Children.Count > 0); // Uncomment to filter out <Member> elements with no child members.
    .ToList();


foreach (var ele in memb_L0)
{
    Console.WriteLine("Parent: \"{0}\", ", ele.Parent.Attribute("Name")?.Value);

    var memb_L1 = ele.Parent.DescendantsUntil(e => e.Name == name).Select(e => (Children: e, Nephew: e.DescendantsUntil(c => c.Name == name).ToList())).ToList();

    memb_L1.ForEach(i => Console.WriteLine("Children: \"{0}\", Nephews: {1}",                              
                               i.Children.Attribute("Name")?.Value,
                               i.Nephew.Count == 0
                               ? "None" :
                               string.Join(",",
                                           i.Nephew.Select(c => $"\"{c.Attribute("Name")?.Value}\""))));

}

Here's the output:

Parent: "3bool1",
Children: "bool1", Nephews: None
Children: "bool2", Nephews: None
Children: "bool3", Nephews: None
Children: "3bool1", Nephews: "bool1","bool2","bool3"
Parent: "int7"
  • Remember that Stack Overflow isn't just intended to solve the immediate problem, but also to help future readers find solutions to similar problems, which requires understanding the underlying code. This is especially important for members of our community who are beginners, and not familiar with the syntax. Given that, **can you [edit] your answer to include an explanation of what you're doing** and why you believe it is the best approach? – Jeremy Caney Apr 18 '23 at 01:14