В моем предыдущем вопросе: ссылка,
Я научился использовать метод расширения DescendantsUntil() для поиска самых верхних элементов (Parent) и их самых верхних потомков (Children).
Предположим теперь, что у нас есть больше потомков (родитель, дети, племянники и т. д.):
<?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 = ""3bool"" 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 = ""3bool"" 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>
Используя метод расширения DescendantsUntil(), я могу легко фильтровать родительские и дочерние элементы.
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();
Теперь, как я могу использовать DescendantsUntil() для извлечения элементов Parent, Children, Nephews и вообще, как извлечь всех потомков, если есть еще один вложенный?





Попробуйте такой код:
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);
}
}
}
}
Пожалуйста, всегда объясняйте код. Объясните будущим читателям, где найти основные части, отвечающие на вопрос.
Я не могу понять, как использовать этот код, извините.
Код анализирует XML и создает дерево (класс), корнем которого является вершина дерева.
Я нашел решение сам (может быть, это может быть полезно для вас).
Если вы хотите использовать метод расширения «DescendantsUntil», чтобы найти больше вложенных уровней, вам просто нужно снова вызвать его внутри оператора foreach, как в следующем рабочем коде:
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}\""))));
}
Вот результат:
Parent: "3bool1",
Children: "bool1", Nephews: None
Children: "bool2", Nephews: None
Children: "bool3", Nephews: None
Children: "3bool1", Nephews: "bool1","bool2","bool3"
Parent: "int7"
Помните, что Stack Overflow предназначен не только для решения непосредственной проблемы, но и для того, чтобы помочь будущим читателям найти решения похожих проблем, что требует понимания базового кода. Это особенно важно для членов нашего сообщества, которые являются новичками и не знакомы с синтаксисом. Учитывая это, можете ли вы отредактировать свой ответ, включив в него объяснение того, что вы делаете, и почему вы считаете, что это лучший подход?
У вас есть древовидная структура, и вы должны использовать древовидные алгоритмы для разбора по частям. Вероятно, вам нужен рекурсивный алгоритм.