LINQ to XML - 包含父项属性的自定义对象列表

时间:2013-09-18 12:58:40

标签: c# linq linq-to-xml

LINQ学习曲线今晚证明过于陡峭,所以我再来这里寻求帮助。很多人都感激不尽。

这是我目前的代码;

_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列表。我转换MyObjs中的Level3元素。
  2. 我只希望Level2元素中的Level3元素ID为12345
  3. 我想在每个子级别Level3元素中使用Level2(id = 12345)元素中的一些属性
  4. 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属性(使用'let')。我想使用Let以便我可以检查null等

    再次,谢谢

3 个答案:

答案 0 :(得分:2)

您应该使用SelectMany获取level2元素的level3项。查询语法等效项为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")
}

更新:为什么我在解决方案中提到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")
    });

这里的关键点是通过从每个匹配的level2元素中选择所有level3后代到单个序列来展平查询。

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 :(得分: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的最后一个事实推断出正确的类型

答案 2 :(得分: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")
                  }
                 );