3

I'm converting some code that currently uses an XmlWriter to create a document to instead return an XElement of the content.

So far, I'm enjoying structuring the code in a way that mimics the structure of the document, but there is content that was written using XmlWriter.WriteRaw to avoid re-xmlizing the xml. I can't find any equivalent in the System.Xml.Linq namespace. Does one exist?

redman
  • 1,510
  • 17
  • 33

2 Answers2

4

XElement.Parse() should do the trick.

For example:

XElement e = new XElement("root",
    new XElement("child",
        XElement.Parse("<big><blob><of><xml></xml></of></blob></big>"),
        new XElement("moreXml")));
Kev
  • 118,037
  • 53
  • 300
  • 385
  • *feeling sheepish* I guess I should have said "I can't find any equivalent in the System.Xml.Linq namespace...except for the one somewhat-obvious method." Thanks! – redman Apr 19 '11 at 22:06
  • @oakskc - lol after a long day sometimes you can't see the wood for the trees :) – Kev Apr 19 '11 at 22:10
  • isn't this re-xmlizing the xml, which is what the question asks to avoid? – drzaus Jan 13 '14 at 17:56
  • @drzaus - re-read this bit but start further back in the sentence: *"but there is content that was written using `XmlWriter.WriteRaw`"* - what he means is that there are raw XML strings (possibly not string literals, maybe string variables). These strings are written to the `XmlWriter` using `XmlWriter.WriteRaw`. `XElement.Parse` does the equivalent thing, which is what he/she asked for. – Kev Jan 14 '14 at 03:29
  • @Kev my understanding of `XElement.Parse` (may be wrong) is that it _parses_ a string, including validation, turning it into an XML construct -- isn't this is "re-xmlizing"? [`WriteRaw`](http://msdn.microsoft.com/en-us/library/0755ytay(v=vs.110).aspx) _doesn't validate_ it or escape before writing. Background: I got here looking for a way to "merge" an existing XML string into (the middle of) another XML construct, and I didn't want to waste time parsing what I knew was correct. [I'm pretty sure `Parse` is doing some possibly unnecessary work](http://stackoverflow.com/a/21098659/1037948). – drzaus Jan 14 '14 at 15:34
  • @drzaus - thing is I don't know what OP is doing with the resultant XML (perhaps it eventually gets loaded into an `XMLDocument` for XPath querying, I don't know, but if it does the XML will still be "XML'ised" - i.e. parsed and validated). Using an `XmlWriter` with `XmlWriter.WriteRaw` is often used as a fast and dirty way to generate a large blob of XML using raw strings with perhaps concatenation of values (for attributes or element values) from a database. – Kev Jan 14 '14 at 20:34
  • Anyway, whatever OP is doing with this XML, something has clearly changed in his/her app and the XML now needs to be represented in an `XElement` object (possibly to be able to perform LINQ to XML queries - again who knows). Ultimately the only way to load a string into an `XElement` is to use either the `Parse(string)` method or one of the `Load()` overloaded methods - which in both cases cause the XML to be parsed...you just can't avoid that if you want to load raw XML into that object. – Kev Jan 14 '14 at 20:35
  • To address the final part of your comment *"I didn't want to waste time parsing what I knew was correct"* - can I just say one thing - premature optimisation :) - get your app working, if you think this method is hurting your app then profile and find out, before spending hours resolving this. – Kev Jan 14 '14 at 20:36
  • Also regardless of the above, if you have a foreign XML string, and you already know it is "valid", you will always have to "parse" the XML to build the internal data structures contained within XElement (or even XmlDocument/XmlNode,...). Otherwise, how else would `XElement` know how to build child `XElements` and `XAttributes` from from that XML string. The XML doesn't live as a string inside XElement, it lives as a bunch of objects created by "parsing" your XML string. I hope this helps. – Kev Jan 14 '14 at 20:42
  • @Kev in my case i was saving captured SOAP requests inside of a larger logging construct (XML like `##existing_xml##`) which got saved as a string anyway. Even without trying the significantly more verbose SOAP xml, I got a 3x boost by using `Replace` rather than `Parse`. By pointing this out where most people come to learn the answer in the first place, it's no longer a question of 'premature optimization' since readers won't have tried anything else yet. I'm not saying your answer is wrong. – drzaus Jan 14 '14 at 20:56
  • I appreciate what you're saying, hopefully your answer and my own will help users decide what suits them best - horses for courses, context and all that. I have a scheduled task that captures raw XML strings from a remote service and logs them inside a bunch of other XML. It processes several hundred status requests, the bottle neck is waiting for the remote service to respond (can be 15-30 seconds per request), re-parsing their XML is the least of my performance worries, even with responses as large as 20-30KB. Saving 50-100ms wouldn't make my task noticeably faster. – Kev Jan 14 '14 at 22:25
2

Caveat: only applicable if your purpose is simply for rendering the XML string, and you're sure the contents are already XML

Since XElement.Parse "re-xmlizes" the already existing XML, you could set the contents to a 'placeholder' value (as suggested here) and replace it in the rendered output:

var d = new XElement(root, XML_PLACEHOLDER);
var s = d.ToString().Replace(XML_PLACEHOLDER, child);

Note that this won't guarantee 'pretty formatting' unless child already has it.

Testing this in LinqPad seems to indicate that 'Replacing' is faster than using Parse:

void Main()
{
    // testing:
    // * https://stackoverflow.com/questions/1414561/how-to-add-an-existing-xml-string-into-a-xelement
    // * https://stackoverflow.com/questions/16586443/adding-xml-string-to-xelement
    // * https://stackoverflow.com/questions/587547/how-to-put-in-text-when-using-xelement
    // * https://stackoverflow.com/questions/5723146/is-there-an-xelement-equivalent-to-xmlwriter-writeraw

    var root = "root";
    var childContents = "<name>Fname</name><age>23</age><sex>None of your business</sex>";
    var child = "<child>" + childContents + "</child>";

    parse(root, child, true);
    replace(root, child, true);

// this fails, per https://stackoverflow.com/questions/16586443/adding-xml-string-to-xelement
try {
        parse(root, childContents, true);
    } catch(Exception ex) {
        ex.Dump();
    }
// this works, but again, you don't get the pretty formatting
    try {
        replace(root, childContents, true);
    } catch(Exception ex) {
        ex.Dump();
    }

    "Xml Parsing".Vs(new [] { "parse", "replace" }
        , n => parse(root, child, false)
        , n => replace(root, child, false)
    );
}

// Define other methods and classes here
void parse(string root, string child, bool print) {
    var d = new XElement(root, XElement.Parse(child));
    var s = d.ToString();
    if(print) s.Dump("Parse Result");
}

const string XML_PLACEHOLDER = "##c##";
void replace(string root, string child, bool print) {
    var d = new XElement(root, XML_PLACEHOLDER);
    var s = d.ToString().Replace(XML_PLACEHOLDER, child);
    if(print) s.Dump("Replace Result");
}

where Vs is a wrapper function for running each delegate 10000 times inside a stopwatch.

Community
  • 1
  • 1
drzaus
  • 24,171
  • 16
  • 142
  • 201