如何获取XElement的值而不是所有子节点的值?

30

示例xml:

<parent>
<child>test1</child>
<child>test2</child>
</parent>
如果我使用XElement的parent.Value属性,我会得到"test1test2"。 但我期望得到的是""(因为没有文本/值)。
我应该查找XElement的哪个属性?
5个回答

26

在查找<parent>元素中的文本数据时,您应该查找具有NodeType属性等于XmlNodeType.Text的子节点。这些节点将是XText类型的。以下示例说明了这一点:

var p = XElement
    .Parse("<parent>Hello<child>test1</child>World<child>test2</child>!</parent>");

var textNodes = from c in p.Nodes()
                where c.NodeType == XmlNodeType.Text
                select (XText)c;

foreach (var t in textNodes)
{
    Console.WriteLine(t.Value);
}

更新: 如果您只想获取第一个文本节点(如果有的话),这里有一个使用LINQ方法调用而不是查询语法的示例:

var firstTextNode = p.Nodes().OfType<XText>().FirstOrDefault();
if (firstTextNode != null)
{
    var textValue = firstTextNode.Value;
    ...do something interesting with the value
}

注意:在这种情况下,使用 First()FirstOrDefault() 比使用 Count() > 0 更有效率。Count 总是枚举整个集合,而 FirstOrDefault() 只会枚举直到找到匹配项。


CDATA部分怎么样?(XmlNodeType.CDATA) - dtb
@dtb - 你可以将cdata部分作为XCData节点找到。你可以轻松地扩展查询以返回文本和CData。 - Peter Lillevold
我用了和你类似的方式解决了这个问题...但是你的答案看起来更加优雅。我的代码:if (parent.Nodes() != null && parent.Nodes().OfType<XText>().Count() > 0) parentValue = parent.Nodes().OfType<XText>().First().Value; - NiTiN

9
很令人惊讶,微软的某个程序员认为将所有文本值作为连接和未分隔字符串返回会很有用。幸运的是,另一位微软开发者编写了一个XElement扩展来返回他们称之为文本节点的“浅层值” ,可以在此处找到。对于那些不喜欢点击链接的人,下面是该函数...
    public static string ShallowValue(this XElement element)
    {
        return element
               .Nodes()
               .OfType<XText>()
               .Aggregate(new StringBuilder(),
                          (s, c) => s.Append(c),
                          s => s.ToString());
    }

你可以这样调用它,因为它会返回所有的空白(或者说,你也可以在扩展中对其进行修剪)。

// element is a var in your code of type XElement ...
string myTextContent = element.ShallowValue().Trim();

1
哇!这真是疯狂的想法。Value 应该是当前元素。ToString()some other value 应该是当前元素加上子元素。 - Pure.Krome

8
你可以将parent节点中所有XText节点的值连接起来:
XElement parent = XElement.Parse(
    @"<parent>Hello<child>test1</child>World<child>test2</child>!</parent>");

string result = string.Concat(
    parent.Nodes().OfType<XText>().Select(t => t.Value));

// result  ==  "HelloWorld!"

作为比较:

// parent.Value  ==  "Hellotest1Worldtest2!"

// (parent.HasElements ? "" : parent.Value)  ==  ""

Concat无法给出期望的结果,因为它不会连接列表中的字符串,而是其参数的“类型名称”,在此处为字符串的IEnumerable。相反,你可能需要使用string.Join("", element.Nodes().OfType<XText>().Select(t => t.Value).ToArray()); - Evariste

1
// Create the XElement
XElement parent = XElement.Parse(
    @"<parent>Hello<child>test1</child>World<child>test2</child>!</parent>");

// Make a copy
XElement temp=new XElement(parent);

// remove all elements but root
temp.RemoveNodes();

// now, do something with temp.value, e.g.
Console.WriteLine(temp.value);

1

msdn说:

一个包含此元素所有文本内容的字符串。如果有多个文本节点,则它们将被连接。

因此,这种行为是可以预期的。

您可以通过执行以下操作来解决问题:

string textContent = parent.HasElements ? "" : parent.Value;

3
混合内容元素怎么处理?例如:<parent>Hello<child>test1</child>World<child>test2</child>!</parent> - dtb
@dtb: parent包含2个Child孩子和2个TextNode孩子。 - H H
我的脑袋已经炸了,我不知道在 XML 中这是允许的。 - Femaref

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接