如何为xml元素之间的数据编写linq到xml

本文关键字:xml linq 数据 之间 元素 | 更新日期: 2023-09-27 18:25:08

我正在尝试解析一些类似于以下内容的XML:

<document>
    <headings>
        Important heading stuff.
    </headings>
    <startGroup group="1" />
        <startItem value="1" />Item one stuff<endItem />
        <blockofdata>
            <startItem value="2" />Item two stuff<endItem />
            <startItem value="3" />Item three stuff<endItem />
        </blockofdata>
        <startItem value="4" />Item four stuff<endItem />
    <endGroup />
    <startGroup group="2" />
        <startItem value="1" />Item one stuff<endItem />
        <startItem value="2" />Item two stuff<endItem />
        <startItem value="3" />Item three stuff<endItem />
    <endGroup />
</document>

我无法找到一个linq-to-xml语句来获得我想要的内容。我需要把结构弄平。因此,假设上面的XML,我想得到这个POCO:的列表

class Items
{
    public int GroupNumber {get;set;} // group property of startGroup
    public int ItemNumber {get;set;} // value property of startItem
    public string ItemText {get;set;}  // data between i
}

如何编写linq-to-xml语句,将属性之间的数据拉入上述项,同时从startGroup/endGroup之间获取数据,并从startItem/endItem之间获取数据?我在这方面已经花了几个小时,即将改用XML流读取器并以老式的方式对其进行解析。

如何为xml元素之间的数据编写linq到xml

这里的关键是使用ElementsAfterSelf()NodesAfterSelf()方法来获取同级节点以及TakeWhile()谓词,以便在适当的时候停止枚举。

首先是助手方法:

public static Items ItemsFromStartItem(XElement start, XElement group)
{
    return new Items
    {
        GroupNumber = (int)group.Attribute("group"),
        ItemNumber = (int)start.Attribute("value"),
        ItemText = start.NodesAfterSelf()
            .TakeWhile(n => n.NodeType != XmlNodeType.Element
                         || ((XElement)n).Name != "endItem")
            .OfType<XText>()
            .Select(t => t.Value)
            .Single()
    };
}
public static IEnumerable<Items> ItemsFromBlockOfData(
    XElement block, XElement group)
{
    return block.Elements("startItem")
        .Select(start => ItemsFromStartItem(start, group));
}

还有神奇的查询。

var query = doc.Descendants("startGroup")
    .SelectMany(group => group.ElementsAfterSelf()
        .TakeWhile(e => e.Name != "endGroup")
        .SelectMany(e => e.Name == "startItem"
            ? new[] { ItemsFromStartItem(e, group) }
            : ItemsFromBlockOfData(e, group))
    );

现在我希望您没有自己设计这个XML。。。这种东西真的会把人推向边缘。)