30

Sample xml:

<parent>
<child>test1</child>
<child>test2</child>
</parent>

If I look for parent.Value where parent is XElement, I get "test1test2". What I am expecting is "". (since there is no text/value for .

What property of XElement should I be looking for?

NiTiN
  • 1,022
  • 1
  • 16
  • 25

5 Answers5

26

When looking for text data in the <parent> element you should look for child nodes that have NodeType properties equal to XmlNodeType.Text. These nodes will be of type XText. The following sample illustrates this:

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);
}

Update: if all you want is the first Text node, if any, here's an example using LINQ method calls instead of query comprehension syntax:

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

Note: using First() or FirstOrDefault() will be more performant than Count() > 0 in this scenario. Count always enumerates the whole collection while FirstOrDefault() will only enumerate until a match is found.

Peter Lillevold
  • 33,668
  • 7
  • 97
  • 131
  • What about CData sections? (XmlNodeType.CDATA) – dtb Nov 22 '10 at 23:51
  • @dtb - you find cdata sections as `XCData` nodes. You could easily extend the query to return both Text and CData. – Peter Lillevold Nov 23 '10 at 07:55
  • I solved the problem pretty much your way... but your answer looks more elegant. My Code: if (parent.Nodes() != null && parent.Nodes().OfType().Count() > 0) parentValue = parent.Nodes().OfType().First().Value; – NiTiN Nov 23 '10 at 16:45
9

It is amazing that a coder somewhere at Microsoft thought that returning all text values as a concatenated and undelimited string would be useful. Luckily, another MS developer wrote an XElement extension to return what they call the "Shallow Value" of the text node here. For those who get the willies from clicking on links, the function is below...

    public static string ShallowValue(this XElement element)
    {
        return element
               .Nodes()
               .OfType<XText>()
               .Aggregate(new StringBuilder(),
                          (s, c) => s.Append(c),
                          s => s.ToString());
    }

And you call it like this, because it gives you all the whitespace too (or, come to think of it, you could trim it in the extension, whatever)

// element is a var in your code of type XElement ...
string myTextContent = element.ShallowValue().Trim();
Steve Hibbert
  • 2,045
  • 4
  • 30
  • 49
  • 1
    WOW! crazy cray cray that this was considered. `Value` should be current element. `ToString()` or `some other value` should be current element + children elements. – Pure.Krome Sep 23 '20 at 01:52
8

You could concatenate the value of all XText nodes in parent:

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!"

For comparison:

// parent.Value  ==  "Hellotest1Worldtest2!"

// (parent.HasElements ? "" : parent.Value)  ==  ""
dtb
  • 213,145
  • 36
  • 401
  • 431
  • `Concat` cannot give he expected result, since it won't concat the strings in the list, but the _type name_ of its parameter, here an `IEnumerable` of strings. `string.Join("", element.Nodes().OfType().Select(t => t.Value).ToArray());` is probaby what you need instead. – Evariste Jun 21 '17 at 15:39
1

msdn says:

A String that contains all of the text content of this element. If there are multiple text nodes, they will be concatenated.

So the behaviour is to be expected.

You could solve your problem by doing:

string textContent = parent.HasElements ? "" : parent.Value;
Femaref
  • 60,705
  • 7
  • 138
  • 176
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);
Mehrdad Mirreza
  • 984
  • 12
  • 20