Linq to XML,multiple(子元素)where和group子句

时间:2011-09-09 01:04:32

标签: linq-to-xml

作为Linq的新手,我花了太多时间在这上面,所以是时候向专家们寻求一些指示了:)

总之,我想使用两个where和一个group by子句来过滤输入,创建我可以投入的输出

new <SMO<SMO<Plane>>(id, items)

类型集合。 (SMO是我自己的ObservableCollection实现,通过给出Title和'root'XElements来实例化。)

我的XML输入类似于此;

<?xml version="1.0" encoding="utf-8"?>
<manifest>
  <id>manifest id</id>
  <locale>US</locale>
  <plane>
    <id>First</id>
    <definition>
      <description>plane description</description>
      <type>plane type</type>
      <item>
        <name>Item 1</name>
        <condition>item condition</condition>
        <description>item description</description>
        <ondelivery>
          <c>instant</c>
          <p1>delivery parameter 1</p1>
          <p2>delivery parameter 2</p2>
        </ondelivery>
      </item>
      <item>
        <name>Item 2</name>
        <condition>item condition</condition>
        <description>item description</description>
        <ondelivery>
          <c>email</c>
          <p1>delivery parameter 1</p1>
        </ondelivery>
      </item>
    </definition>
  </plane>
  <plane>
    <id>Second</id>
    <definition>
      <description>plane description</description>
      <type>plane type</type>
      <item>
        <name>Item 1</name>
        <condition>item condition</condition>
        <description>item description</description>
        <ondelivery>
          <c>instant</c>
          <p1>delivery parameter 1</p1>
          <p2>delivery parameter 2</p2>
        </ondelivery>
      </item>
      <item>
        <name>Item 2</name>
        <condition>item condition</condition>
        <description>item description</description>
        <ondelivery>
          <c>email</c>
          <p1>delivery parameter 1</p1>
        </ondelivery>
      </item>
      <item>
        <name>Item 3</name>
        <condition>item condition</condition>
        <description>item description</description>
        <ondelivery>
          <c>instant</c>
          <p1>delivery parameter 1</p1>
          <p2>delivery parameter 1</p2>
          <p3>delivery parameter 1</p3>
        </ondelivery>
      </item>
    </definition>
  </plane>
</manifest>

基本上,每个平面都有一个定义,其中包含任意数量的 item 。每个包含一个 c ,后跟任意数量的 p N.

我需要做的是根据它的 id 过滤掉一个平面。通过运行类似的东西,这是微不足道的。

    var oput = (from c in doc.Element("manifest").Elements("plane")
        where (string)c.Element("id") == "Second"
        select c);

当我必须根据每个 c 属性进一步过滤此问题时,会出现问题(但同时有一个完整的平面要使用的元素)。例如,从上面的源代码我需要从 plane “second”中拉出所有 c 等于“instant”。

从此我希望/需要类似于

的输出
<?xml version="1.0" encoding="utf-8"?>
<manifest>
  <id>manifest id</id>
  <locale>US</locale>
  <plane>
    <id>Second</id>
    <definition>
      <description>plane description</description>
      <type>plane type</type>
      <item>
        <name>Item 1</name>
        <condition>item condition</condition>
        <description>item description</description>
        <ondelivery>
          <c>instant</c>
          <p1>delivery parameter 1</p1>
          <p2>delivery parameter 2</p2>
        </ondelivery>
      </item>
      <item>
        <name>Item 3</name>
        <condition>item condition</condition>
        <description>item description</description>
        <ondelivery>
          <c>instant</c>
          <p1>delivery parameter 1</p1>
          <p2>delivery parameter 1</p2>
          <p3>delivery parameter 1</p3>
        </ondelivery>
      </item>
    </definition>
  </plane>
</manifest>

以XElement的形式(因为我正在创建一个 new Plane 类,它将此作为输入)。

到目前为止我提出的最好的是

    var oput = (from c in doc.Element("manifest").Elements("plane")
        where (string)c.Element("id") == "Second"
        from d.Elements("definition").Elements("item").Elements("c")
        where (string)d.Element("c") == "instant"
        select d);

它可以正确过滤,但它给我的输出是可以理解的,是 ondelivery 的IEnumerable。

我尝试过使用c,d.Parent,new XElement(“plane”,d.Parent.Parent.Parent.Element(“id”),d.Parent))等实际选择。在,但我完全无法得到我想要的那种输出。

作为奖励,我想根据 plane * id *同时对这些进行分组。我已经掌握了group by子句,但不知道如何格式化它给出的输出。理想情况下,我需要输出为(String,IEnumerable)像(“Second”,(XElement(Item 1),XElement(Item 2)))而我似乎总是以(IEnumerable(String,IEnumerable))结束)外部IEnumerable的项目数为1。

1 个答案:

答案 0 :(得分:0)

我只会使用原始查询选择plane元素,然后添加一些代码来删除不需要的item元素,如下所示:

var oput = (from c in doc.Descendants("plane")
            where (string)c.Element("id") == "Second"
            select c);

foreach (var plane in oput)
{
    foreach (var item in plane.Descendants("item").Where((x) => x.Descendants("c").First().Value != "instant").ToList() )
    {
        item.Remove();
    }
    Console.WriteLine(plane); // or whatever you need to do with each plane.
}

对项目循环调用ToList()非常重要,因为没有它,foreach会在其中一个项目被删除时中断。基本上调用ToList()会使列表的副本更改为Remove方法不会修改。