你如何为XML元素之间的数据编写linq-to-xml?

时间:2012-02-18 23:46:46

标签: c# xml linq linq-to-xml

我正在尝试解析一些看起来与此类似的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流阅读器并以老式的方式解析它。

1 个答案:

答案 0 :(得分:2)

此处的关键是使用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 ......这种东西可以真正推动某人超越边缘。 ;)