Предположим, что у вас есть такой XML:
<data>
<messages>
<message name="Person" id="P">
<field name="FirstName" required="Y" />
<field name="LastName" required="Y" />
<field name="Sex" required="N" />
</message>
<message name="Car" id="C">
<field name="Make" required="Y" />
<field name="Model" required="Y" />
<field name="Year" required="N" />
</message>
</messages>
</data>
Используя Linq, как бы вы получили список всех необходимых имен полей для Person?
Я только начал играть с LINQ/XML сегодня, это примерно так, как я получил.
var q = from c in loaded.Descendants("field")
where (string)c.Attribute("required") == "Y" &&
// how to check the parent node (message) has an attribute (id="P")
select (string)c.Attribute("name");
foreach (string name in q)
Console.WriteLine(name);
Этот ответ совершенно неверен, учитывая, что вопроситель изменил свой xml. Должен ли я удалить это.
var q = from c in loaded.Descendants("field")
where (string)c.Attribute("required") == "Y" &&
c.Parent.Attribute("id").Value == "P"
select (string)c.Attribute("name");
Добавление Xml, который я использовал, потому что есть некоторая путаница в правильном решении.
XDocument loaded = XDocument.Parse(@"
<message name=""Person"" id=""P"">
<field name=""FirstName"" required=""Y"" />
<field name=""LastName"" required=""Y"" />
<field name=""Sex"" required=""N"" />
<message name=""Car"" id=""C"">
<field name=""Make"" required=""Y"" />
<field name=""Model"" required=""Y"" />
<field name=""Year"" required=""N"" />
</message>
</message>");
Я добавил корневой элемент и закрывающий тег для сообщений, чтобы сделать XML действительным:
XDocument loaded = XDocument.Parse(@"
<messages>
<message name=""Person"" id=""P"">
<field name=""FirstName"" required=""Y"" />
<field name=""LastName"" required=""Y"" />
<field name=""Sex"" required=""N"" />
</message>
<message name=""Car"" id=""C"">
<field name=""Make"" required=""Y"" />
<field name=""Model"" required=""Y"" />
<field name=""Year"" required=""N"" />
</message>
</messages>");
Вместо того, чтобы искать все поля, а затем проверять родительский элемент для каждого из них, найдите один из родителей, с которым вы перепробованы, чтобы у вас было меньше полей для проверки:
IEnumerable<string> fields =
loaded.Root.Elements()
.Where(m => m.Attribute("id").Value == "P")
.Single()
.Elements("field")
.Where(f => f.Attribute("required").Value == "Y")
.Select(f => f.Attribute("name").Value);
Edit:
Добавлен спецификатор "field" для дочерних элементов, если элемент сообщения содержит любые другие типы элементов.
Изменить 2:
Я собрал рабочий пример с подмножеством фактических данных:
XDocument loaded = XDocument.Parse(@"
<fix major=""4"" minor=""4"">
<header>
</header>
<trailer>
</trailer>
<messages>
<message name=""ResendRequest"" msgtype=""2"" msgcat=""admin"">
<field name=""BeginSeqNo"" required=""Y"" />
<field name=""EndSeqNo"" required=""Y"" />
</message>
<message name=""Reject"" msgtype=""3"" msgcat=""admin"">
<field name=""RefSeqNum"" required=""Y"" />
<field name=""RefTagID"" required=""N"" />
<field name=""RefMsgType"" required=""N"" />
<field name=""SessionRejectReason"" required=""N"" />
<field name=""Text"" required=""N"" />
<field name=""EncodedTextLen"" required=""N"" />
<field name=""EncodedText"" required=""N"" />
</message>
</messages>
</fix>");
IEnumerable<string> fields =
loaded.Root.Element("messages").Elements("message")
.Where(m => m.Attribute("name").Value == "Reject")
.Single()
.Elements("field")
.Where(f => f.Attribute("required").Value == "Y")
.Select(f=>f.Attribute("name").Value);
вы можете удалить отдельный цикл foreach и уродливый листинг, сделав также следующее:
(from c in myXML.Descendants("field")
where c.Attribute("required").Value == "Y" &&
c.Parent.Attribute("id").Value == "P"
select c.Attribute("name").Value).ToList().ForEach(s => Console.WriteLine(s.ToString()));
Edit:
как проблема с обнуленными нулевыми или необязательными атрибутами, вот пример того, как добавить метод расширения для обработки нулевых атрибутов, вы можете передать его по умолчанию, если он не существует (это второй параметр в метод расширения).
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml.Linq;
using System.Text;
namespace MyTestApp
{
class Program
{
static void Main(string[] args)
{
XDocument myXML = XDocument.Load(@"C:\file.xml");
(from c in myXML.Descendants("field")
where c.Attribute("required")
.GetAttributeValueOrDefault("N") == "Y" &&
c.Parent.Attribute("id").Value == "P"
select
c.Attribute("name").Value).ToList().ForEach(s => Console.WriteLine(s.ToString()));
Console.ReadLine();
}
}
public static class XLinqHelper
{
// extension method that handles an xattribute and returns the provided default if the Xattrib is null
public static string GetAttributeValueOrDefault(this XAttribute s, string defaultValue)
{
string retVal;
if (s == null)
retVal = defaultValue;
else
retVal = s.Value;
return retVal;
}
}
}
Вы используете метод Ancestors() или Parent, как определено System.Xml.Linq.XNode и System.Xml.Linq.XObject