What's the best way to get the contents of the mixed body
element in the code below? The element might contain either XHTML or text, but I just want its contents in string form. The XmlElement
type has the InnerXml
property which is exactly what I'm after.
The code as written almost does what I want, but includes the surrounding <body>
...</body>
element, which I don't want.
XDocument doc = XDocument.Load(new StreamReader(s));
var templates = from t in doc.Descendants("template")
where t.Attribute("name").Value == templateName
select new
{
Subject = t.Element("subject").Value,
Body = t.Element("body").ToString()
};
I wanted to see which of these suggested solutions performed best, so I ran some comparative tests. Out of interest, I also compared the LINQ methods to the plain old System.Xml method suggested by Greg. The variation was interesting and not what I expected, with the slowest methods being more than 3 times slower than the fastest.
The results ordered by fastest to slowest:
Method
I used a single XML document with 20 identical nodes (called 'hint'):
The numbers shown as seconds above are the result of extracting the "inner XML" of the 20 nodes, 1000 times in a row, and taking the average (mean) of 5 runs. I didn't include the time it took to load and parse the XML into an
XmlDocument
(for the System.Xml method) orXDocument
(for all the others).The LINQ algorithms I used were: (C# - all take an
XElement
"parent" and return the inner XML string)CreateReader:
Aggregate with string concatenation:
StringBuilder:
String.Join on array:
String.Concat on array:
I haven't shown the "Plain old System.Xml" algorithm here as it's just calling .InnerXml on nodes.
Conclusion
If performance is important (e.g. lots of XML, parsed frequently), I'd use Daniel's
CreateReader
method every time. If you're just doing a few queries, you might want to use Mike's more concise Aggregate method.If you're using XML on large elements with lots of nodes (maybe 100's), you'd probably start to see the benefit of using
StringBuilder
over the Aggregate method, but not overCreateReader
. I don't think theJoin
andConcat
methods would ever be more efficient in these conditions because of the penalty of converting a large list to a large array (even obvious here with smaller lists).Personally, I ended up writing an
InnerXml
extension method using the Aggregate method:My client code is then just as terse as it would be with the old System.Xml namespace:
With all due credit to those who discovered and proved the best approach (thanks!), here it is wrapped up in an extension method:
I ended up using this:
Wondering if (notice I got rid of the b+= and just have b+)
might be slightly less efficient than
Not 100% sure...but glancing at Aggregate() and string.Join() in Reflector...I think I read it as Aggregate just appending a returning value, so essentially you get:
string = string + string
versus string.Join, it has some mention in there of FastStringAllocation or something, which makes me thing the folks at Microsoft might have put some extra performance boost in there. Of course my .ToArray() call my negate that, but I just wanted to offer up another suggestion.
Keep it simple and efficient: