2013-09-18 4 views
2

Кривая обучения LINQ сегодня слишком крутая, поэтому я снова прихожу сюда за помощью. Очень благодарна заранее.LINQ to XML - список пользовательских объектов с атрибутами от родителя

Вот мой текущий код;

_myList = (
from listLv2 in 
    _documentRoot.Descendants("Level1").Descendants("Level2") 
where 
    (string)listLv2.Attribute("id") == "12345" 

let attrib_Colour = listLv2.Attribute("Colour") 
let attrib_ID = listLv2.Attribute("id") 

//select listLv2.Descendants("Level3") <---- not working 
select new MyObj 
{ 
let attrib_ChildID = ???..Attribute("id") 

ParentColour = attrib_Colour.Value, 
ParentID = attrib_ID.Value, // in this case 12345 
ChildID = attrib_ChildID 
}).ToList<MyObj>(); 

Вот что я пытаюсь достичь;

  1. создать список MyObjs. Я конвертирую элементы Level3 в MyObjs.
  2. Я хочу только элементы LEVEL3 в элементе Level2 с идентификатором 12345
  3. Я хочу использовать некоторые атрибуты из Level2 (ID = 12345) элемента в каждом из детей LEVEL3 элементы

Структура XML выглядит следующим образом.

<root> 
    <Level1> 
<Level2 id="12345" colour="Red"> 
    <Level3 id="0001" /> 
    <Level3 id="0002" /> 
    <Level3 id="0003" /> 
</Level2> 
<Level2 id="45678" colour="Blue"> 
    <Level3 id="0004" /> 
    <Level3 id="0005" /> 
    <Level3 id="0006" /> 
</Level2> 
</Level1> 
</root> 

Объекты в списке должны быть такими;

MyObj.ParentID = 12345 
MyObj.ParentColour = "Red" 
MyObj.ID = 0001 

MyObj.ParentID = 12345 
MyObj.ParentColour = "Red" 
MyObj.ID = 0002 

MyObj.ParentID = 12345 
MyObj.ParentColour = "Red" 
MyObj.ID = 0003 

От и где работают. Он выбирает 1 элемент, Level2 [id = 12345]. Ницца. Атрибуты Level2 работают.

Вот что я не могу решить;

  • Как получить доступ к каждому ребенку LEVEL3 элемент, так что я могу преобразовать его в MyObj
  • Как создать атрибут LEVEL3 (с помощью «пусть»). Я хочу использовать Пусть так, что я могу проверить нуль и т.д.

снова, спасибо

ответ

2

Вы должны использовать SelectMany, чтобы получить LEVEL3 предметы для вашего level2 элемента. Синтаксис запросов эквивалент будет from listLv3 in listLv2.Descendants("Level3"):

from listLv2 in _documentRoot.Descendants("Level1").Descendants("Level2") 
let attrib_ID = (string)listLv2.Attribute("id") 
let attrib_Colour = (string)listLv2.Attribute("colour") // note: lowercase! 
where attrib_ID == "12345" 
from listLv3 in listLv2.Descendants("Level3") 
select new MyObj 
{ 
    ParentColour = attrib_Colour, 
    ParentID = attrib_ID, 
    ChildID = (string)listLv3.Attribute("id") 
} 

UPDATE: Почему я упомянул SelectMany в растворе? Поскольку код выше (если мы забудем о новом переменном диапазоне вводит с let ключевым слова) будет собраны в следующий запрос метод-синтаксический:

_documentRoot 
    .Descendants("Level1") 
    .Descendants("Level2") 
    .Where(listLv2 => (string)listLv2.Attribute("id") == "12345") 
    .SelectMany(listLv2 => listLv2.Descendants("Level3")) // flattening query 
    .Select(listLv3 => new MyObj { 
     ParentColour = (string)listLv3.Parent.Attribute("colour"), 
     ParentID = "12345", // equal to id you are searching for 
     ChildID = (string)listLv3.Attribute("id") 
    }); 

И ключевой момент здесь является уплощением запроса, выбрав все LEVEL3 потомков от каждого соответствуют level2 в одну последовательность.

BTW Рассмотрим также решение XPath:

from listLv3 in root.XPathSelectElements("Level1/Level2[@id='12345']/Level3") 
select new MyObj 
{ 
    ParentColour = (string)listLv3.Parent.Attribute("colour"), 
    ParentID = "12345", 
    ChildID = (string)listLv3.Attribute("id") 
} 
+1

Cheers. Я не мог видеть SelectMany, о котором вы говорили, но этот код мне помог. Еще раз спасибо. – ausgeorge

+0

@ausgeorge этот код скомпилирован в метод 'SelectMany' :) Я добавил несколько объяснений для вас. –

+0

' Descendants' может иметь [удар производительности] (http://mjuraszek.blogspot.in/2013/08/why-not -use-descendants-method.html) для большого xml. – Anirudha

1

Вы используете только select в самом конце, когда вы попали в точку, которая закончена выражение запроса. В принципе, вам необходимо изменить //select <-- not working на еще from и продолжить.Это будет в конечном итоге что-то вроде этого:

_myList = (
from listLv2 in 
    _documentRoot.Descendants("Level1").Descendants("Level2") 
where 
    (string)listLv2.Attribute("id") == "12345" 

let attrib_Colour = listLv2.Attribute("Colour") 
let attrib_ID = listLv2.Attribute("id") 

from listLv3 in listLv2.Descendants("Level3") // <---- should work 

select new MyObj 
{ 
let attrib_ChildID = listLv3.Attribute("id") 

ParentColour = attrib_Colour.Value, 
ParentID = attrib_ID.Value, // in this case 12345 
ChildID = attrib_ChildID 
}).ToList<MyObj>(); 

И заметьте, что ваш последний ToList не нужен явный параметр типа указан, так как C# можно сделать вывод правильный тип из того, что последнее, что вы select являются MyObj s ,

+0

Спасибо! FYI, атрибут attrib_ChildID LET должен находиться между операторами from и select. Я не работаю, когда его внутри выбирает новый {}. – ausgeorge

0
_documentRoot.Elements("Level1") 
      .Elements("Level2") 
      .Where(x=>x.Attribute("id").Value=="12345") 
      .Elements("Level3") 
      .Select(y=> 
        new MyObj 
        { 
         ParentColour = y.Parent.Attribute("colour").Value, 
         ParentID = y.Parent.Attribute("id").Value, 
         ChildID = (string)y.Attribute("id") 
        } 
       ); 
Смежные вопросы