Как заполнить родительскую дочернюю структуру данных из XML, где общие объекты List‹› находятся в родительской и дочерней структурах, используя один запрос LINQ?

У меня есть файл конфигурации XML, который содержит список документов с картами полей для каждого документа. Я хочу использовать LINQ to XML для заполнения внутренней структуры данных, которая представляет документы в виде иерархии структур List‹>.

Атрибут Include определяет, следует ли включать поле.

Вот пример того, как выглядит XML:

<Documents>
  <Document Include="1" DocumentName="Report1" >
    <Field Include="1" OldName="Old1" NewName="New1"/>
    <Field Include="1" OldName="Old2" NewName="New2"/>
    <Field Include="0" OldName="Old3" NewName="New3"/>
  </Document>
  <Document Include="1" DocumentName="Report2" >
    <Field Include="1" OldName="Old1" NewName="New1"/>
    <Field Include="0" OldName="Old3" NewName="New3"/>
  </Document>
</Documents>

Структура данных, представляющая документы, выглядит следующим образом:

class FieldMap
{
    public string OldName { get; set; }
    public string NewName { get; set; }
}

class Document
{
    public string DocumentName { get; set; }
    public List<FieldMap> FieldMaps;
}

private List<Document> Documents;

Ниже приведен код, который выполняет первую часть, заполняя документы:

var ds = from row in elem.Descendants("Document")
         where row.Attribute("Include").Value == "1"
         select new Document
         {
            DocumentName = row.Attribute("DocumentName").Value,
         };

Documents = ds.ToList<Document>();

Я хотел бы изменить приведенный выше код, чтобы он также заполнил структуру списка в каждом документе. Я знаю, что мог бы сделать это, перебирая список документов‹> и запуская в цикле еще один запрос LINQ, однако я бы предпочел делать все как объединенный запрос.


person Matt Spradley    schedule 15.06.2009    source источник
comment
Не поддавайтесь желанию сделать все в одном запросе. Его (возможно) больно читать, в то время как его определенно сложно тестировать или отлаживать.   -  person Frank Schwieterman    schedule 15.06.2009
comment
@ Фрэнк, я не согласен. В этом случае использование подзапроса имеет смысл и чище, чем использование цикла foreach для результата просто для заполнения свойства списка. Если вы считаете, что циклы foreach легче тестировать/отлаживать, используйте их, но я не вижу причин препятствовать подзапросам в LINQ.   -  person CoderDennis    schedule 15.06.2009
comment
Привет, Фрэнк, я склоняюсь к ответу АльберЭйна ниже. Я также склонен согласиться с Деннисом Палмером выше. Можете ли вы предоставить свой подход, если считаете, что он чище? Спасибо   -  person Matt Spradley    schedule 15.06.2009
comment
На мой взгляд, в этом случае чище иметь только один запрос, чем заполнять подэлементы, перебирая записи документа, что позволяет нам избежать чего-то вроде элемента foreach (элемент var в документах).FieldMaps = elem.Descendants (Document).Where (узел => узел.Атрибуты (ИмяДокумента).Значение == элемент.ИмяДокумента).Выбрать (узел => узел.Потомки (Поле)).Где (узел => узел.Атрибуты (Включить).Значение == 1). Select (node ​​=> new FieldMap { OldName = node.Attributes (OldName).Value, NewName = node.Attributes (NewName).Value }).ToList ();   -  person albertein    schedule 15.06.2009


Ответы (1)


Вы пробовали что-то вроде этого:

var ds = from row in elem.Descendants("Document")
         where row.Attribute("Include").Value == "1"
         select new Document
         {
            DocumentName = row.Attribute("DocumentName").Value,
            FieldMaps =    //Solution from here
               (from field in row.Descendants ("Field")
                where field.Attribute("Include").Value == "1"
                select new FieldMap {
                    OldName = field.Attribute("OldName").Value,
                    NewName = field.Attribute("NewName").Value
                }).ToList () //To here
         };

Documents = ds.ToList<Document>();

Некоторые люди говорят, что проще заполнить свойства FielMaps вне запроса LINQ с помощью foreach, я не согласен с этим, но если вам нравится эта альтернатива, она будет выглядеть примерно так:

var ds = from row in elem.Descendants("Document")
         where row.Attribute("Include").Value == "1"
         select new Document
         {
             DocumentName = row.Attribute("DocumentName").Value
         };

Documents = ds.ToList<Document>();

foreach (var document in Documents)
    document.FieldMaps = elem.Descendants("Document")
        .Where(doc => doc.Attributes("DocumentName") == document.DocumentName)
        .Select(doc => doc.Descendants("Fields"))
        .Where(field => field.Attributes("Include").Value == "1")
        .Select(field => new FieldMap
            {
                OldName = field.Attributes("OldName").Value,
                newName = field.Attributes("NewName").Value
            }
        ).ToList();
person albertein    schedule 15.06.2009