Получить дочерний узел на основе атрибута родительского узла - PullRequest
2 голосов
/ 08 мая 2011

У меня есть отчет CrystalReport в XML (извините за многословность, я вырезал большинство образцов данных)

<?xml version="1.0" encoding="UTF-8" ?>
<FormattedReport xmlns = 'urn:crystal-reports:schemas' xmlns:xsi = 'http://www.w3.org/2000/10/XMLSchema-instance'>
<FormattedAreaPair Level="0" Type="Report">
<FormattedAreaPair Level="1" Type="Details">
<FormattedArea Type="Details">
<FormattedSections>
<FormattedSection SectionNumber="0">
<FormattedReportObjects>
<FormattedReportObject xsi:type="CTFormattedField" Type="xsd:string" FieldName="{AIRCRAFT.Tail Number}"><ObjectName>Field2</ObjectName>
<FormattedValue>C-FBCS</FormattedValue>
<Value>C-FBCS</Value>
</FormattedReportObject>
<FormattedReportObject xsi:type="CTFormattedField" Type="xsd:string" FieldName="{AIRCRAFT.Type ID}"><ObjectName>Field8</ObjectName>
<FormattedValue>DHC8</FormattedValue>
<Value>DHC8</Value>
</FormattedReportObject>
<FormattedReportObject xsi:type="CTFormattedField" Type="xsd:unsignedLong" FieldName="{TRIP LEGS.Trip Number}"><ObjectName>Field9</ObjectName>
<FormattedValue>68344</FormattedValue>
<Value>68344.00</Value>
</FormattedReportObject>
</FormattedReportObjects>
</FormattedSection>
</FormattedSections>
</FormattedArea>
</FormattedAreaPair>
<FormattedAreaPair Level="1" Type="Details">
<FormattedArea Type="Details">
<FormattedSections>
<FormattedSection SectionNumber="0">
<FormattedReportObjects>
<FormattedReportObject xsi:type="CTFormattedField" Type="xsd:string" FieldName="{AIRCRAFT.Tail Number}"><ObjectName>Field2</ObjectName>
<FormattedValue>C-FBCS</FormattedValue>
<Value>C-FBCS</Value>
</FormattedReportObject>
<FormattedReportObject xsi:type="CTFormattedField" Type="xsd:string" FieldName="{AIRCRAFT.Type ID}"><ObjectName>Field8</ObjectName>
<FormattedValue>DHC8</FormattedValue>
<Value>DHC8</Value>
</FormattedReportObject>
<FormattedReportObject xsi:type="CTFormattedField" Type="xsd:unsignedLong" FieldName="{TRIP LEGS.Trip Number}"><ObjectName>Field9</ObjectName>
<FormattedValue>68344</FormattedValue>
<Value>68344.00</Value>
</FormattedReportObject>
</FormattedReportObjects>
</FormattedSection>
</FormattedSections>
</FormattedArea>
</FormattedAreaPair>
...
</FormattedAreaPair>
</FormattedReport>

Я пытаюсь использовать запрос LINQ to XML для извлечения значения узел на основе атрибута FieldName родительского узла и поместите их в объект.Не существует уникального атрибута для Value или родительских узлов узлов FormattedReportObject.Пока вот мой код для этого

from fs in xDoc.Descendants("FormattedSection")
select new FlightSchedule
{
  AircraftType = from fos in fs.Descendants("FormattedReportObjects")
                 from fo in fs.Descendants("FormattedReportObject")
                 where fo.Attribute("FieldName").Value.Equals("{AIRCRAFT.Type ID}")
                 from e in fo.Element("Value")
                 select e.Value),
  ....
};

Я получаю ошибки:

Выражение типа 'System.Xml.Linq.XElement' не допускается впосле предложения from в выражении запроса с типом источника 'System.Collections.Generic.IEnumerable'.Ошибка вывода типа при вызове 'SelectMany')

или, если я не получаю ошибку, я ничего не получаю.Буду признателен за любые предложения по улучшению моего запроса.

Ответы [ 2 ]

2 голосов
/ 08 мая 2011

Ваш код имеет несколько проблем.Во-первых, на что жалуется компилятор, как упомянул @MizardX, вы используете fo.Element("Value"), как если бы это была последовательность.Вероятно, вам нужно написать let e = fo.Element("Value") (или пропустить эту часть полностью и напрямую написать select fo.Element("Value").Value).

Другая проблема заключается в том, что ваш XML использует пространство имен, но это не так.Это означает, что вы должны создать XNamespace объект и использовать его везде, где у вас есть имена элементов.

Кроме того, как написан ваш код, AircraftType - это последовательность строк.Я предполагаю, что это не то, что вы хотели.

И, видя, что вы хотите сделать то же самое для разных значений FieldName, вы, вероятно, захотите сделать это методом.все упомянутые выше проблемы исправлены, код должен выглядеть примерно так:

static readonly XNamespace ns = XNamespace.Get("urn:crystal-reports:schemas");

string GetFieldValue(XElement fs, string fieldName)
{
    return (from fo in fs.Descendants(ns + "FormattedReportObject")
            where fo.Attribute("FieldName").Value == fieldName
            let e = fo.Element(ns + "Value")
            select e.Value).Single();
}
…
var flts = (from fs in xDoc.Descendants(ns + "FormattedSection")
            select new FlightSchedule
            {
                AircraftType = GetFieldValue(fs, "{AIRCRAFT.Type ID}"),
                …
            }).ToList();
1 голос
/ 08 мая 2011

fo.Element("Value") возвращает XElement -объект. То, что вы хотите, вероятно, fo.Elements("Value") (обратите внимание на множественное число ').

В сообщении об ошибке сообщалось, что он не знает, как перебирать объект XElement.

Причина, по которой вы не получаете никаких результатов, заключается в том, что XML-файл использует пространства имен. Чтобы найти элементы за пределами пространства имен по умолчанию, вам необходимо добавить префикс пространства имен перед именем узла.

Я также заметил, что вы не используете переменную fos, поэтому цикл не нужен. fs.Decendants() уже дает вам правильный результат.

List<FlightSchedule> flts =
    (from fs in xDoc.Descendants("{urn:crystal-reports:schemas}FormattedSection")
     select new FlightSchedule
     {
         AircraftType =
             (from fo in fs.Descendants("{urn:crystal-reports:schemas}FormattedReportObject")
              where fo.Attribute("FieldName").Value == "{AIRCRAFT.Type ID}"
              from e in fo.Elements("{urn:crystal-reports:schemas}Value")
              select e.Value),
                          ....
     }).ToList();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...