Rant: Don’t return XML in string variables!


I have probably seen a half a dozen article submissions to MSDN in the last year with authors who do the following:




// Bad Bad Bad
[WebMethod]
public string MyLameWebMethod()
{
   
XmlDocument dom = new XmlDocument();
    // load some XML …
    return dom.OuterXml;
}


and then on the client side they do this:



// Bad bad bad
localhost.Service1 proxy = new localhost.Service1();
XmlDocument dom = new XmlDocument();
dom.LoadXml(proxy.MyLameWebMethod());


!!!DON’T DO THIS!!!


Try this instead:



// Better
[WebMethod]
public XmlDocument MyBetterWebMethod()
{
    XmlDocument dom = new XmlDocument();
    // load some XML …
    return dom;
}


Then the client will look like this:



// Better
localhost.Service1 proxy = new localhost.Service1();
XmlNode xml = proxy.MyBetterWebMethod();


Now you are avoiding an added level of serialization/deserialization.  This applies to valid XML only!!  Non-wellformed HTML should be passed as a string that is HTML encoded.


I will say that I have sympathy about this sort of thing and the tools often aren’t much help, but if I can help it, MSDN will never have an article encouraging users to pass XML as an encoded string to/from a Web service.


By the way, I’ve been trying to think of a scenario where returning XML as a string to/from a Web service might make sense…without luck.  If you have one, I would like to hear about it.  I think it is safe to say that for the vast majority of situations, returning XML as an encoded string is not a good practice.


   -Matt

Comments (25)

  1. Jeff Perrin says:

    What happens when you’re accessing the service with another technology, like PHP or ASP? (I really don’t know this…) I’ve created a few XML-RPC based web services that return xml documents as strings, which is exactly what an xml document is… If a PHP client, for example, is accessing a .NET webservice with a return type of XmlDocument, can it easily process it?

  2. brady gaster says:

    AMEN (to the post, not the comment)

  3. David Barnhill says:

    Returning XmlDocument from a web server serializes itself to xml, so if you are accessing the web server from a non-.Net environment it still works OK. What you save is the XmlDocument->String and String->XmlDocument conversions.

  4. Matt Powell says:

    As for interop, As David points out, XmlDocument basically just sticks XML in the body of the envelope so it should definitely be x-platform goodness.

    And by the way, I’ve done this too … back in the early SOAP Toolkit days when a change in toolkit versions changed the wire protocol and we had to live with it until the next revision of our app. But we fixed it as soon as we could – and appropriately repented.

  5. Mike Julier says:

    So is this a question of changing the type of object that gets wrapped around the bytes? It sounds like the bytes don’t change but that perhaps they get copied. Is this what the rant is about?

  6. Matt Powell says:

    The bytes do change. Sending the string sends this:

    <envelope>

    <body>

    &lt;a&gt;&lt;b&gt;blah&lt;/b&gt;&lt;c&gt;blah&lt;/c&gt;&lt;/a&gt;

    </body>

    </envelope>

    Sending the XML sends this:

    <envelope>

    <body>

    <a>

    <b>blah</b>

    <c>blah</c>

    </a>

    </body>

    </envelope>

    (I hope this formats okay in my comments.)

    -Matt

  7. Serban Florea says:

    Well, it is somewhat cleaner (and safer), but it’s not that we dispensed with serialization altogether…

  8. David Taylor says:

    Hi Matt,

    I have been working with the new MS CRM and note that it sends XML between the client and server as strings, which I also found frustrating!

  9. Christoph Schittko says:

    The office team is defined interfaces with stings containing XML for their research and translation web services as well. It would help if service interfaces published by Microsoft Web would follow best practices.

    I am sure many developers new to Web services will follow practices the see in interfaces published by the big guys.

    Christoph

  10. Simon Fell says:

    All you need to do now is make the same fix in your products!, seriously go checkout the SOAP interface for MS-CRM, it blows chunks.

  11. Take Outs for 12 May 2004

  12. kevin says:

    Does this also apply to SignedXML objects? Can they be returned directly as well?

  13. chadbr says:

    What if you don’t want the overhead of creating the DOM on the server in the 1st place?

    If you look @ a typical "read" stack it’s:

    client request => server => some object(s) => database (or data store) request

    (unwinding stack)

    data results => some data format => soap serializer => client

    Why would I want to take on the server overhead of "some data format" being a DOM?

    If I want to process the data from a database before it’s sent back to the client, I probably want to process it in the form of a dataset or datareader — neither returns a DOM –

    So are you suggesting the overhead of creating a DOM so it can be returned in native XML is worth the server overhead?

  14. Yup, agreed about the MS-CRM interfaces. There’s a lot of history around why they’re shaped the way they are, but that doesn’t really matter any more. We (that is, the MS-CRM team) are looking at a bunch of approaches to make this all better in upcoming releases and I’ll try to post some information as I can.

    The problem was really one of timing and a requirement to handle type extensibility in the field. The timing problem is simple – the only tool available when we started MS-CRM was ATL server (and only in beta). Sure, we ended up slipping way past the VS.Net release date, but that’s a different story. The type extensibility problem is that customers want to and need to change the shape of their entities on the fly. There were no types available for ATL server that we could leverage that could automatically extend (struct would never work, it’s hard-coded in the type system), so we ended up choosing strings.

    XML strings as parameters suck. They suck at the programming model level for both the client and the service. Building XML strings is error prone to say the least. But they also suck for type safety reasons on the wire. Trust me, I’ve stared at enough netmon traces of our stuff to get dizzy – it would be tremendously better if we had objects.

    We’re getting there. Slowly, but we’re getting there. All I can recommend right now, and it’s a short term hack, is to look at Dan Roger’s <a href="http://www.microsoft.com/downloads/details.aspx?FamilyID=de0cc46b-4dea-4786-9eb0-8733e41bf5a8&DisplayLang=en">XSDObjectGen</a&gt; tool and build classes from the XSD (it’s what I do when I’m programming against the CRM SOAP interfaces). Although, on that topic, the V1.0 XSD were buggy out of the box. I think v1.2 fixed it and if not I might be persuaded to hand out the right fix.

  15. Shaun McDonnell says:

    What about Classic ASP object incompatability?

    For example, if I have an "Account" object on my .NET side of things and decide make that object a return value in my .NET Web Service, will ASP know how to handle the object? I don’t think so.

    In order to keep my .NET apps object-oriented, we just serialize our objects into Xml like this:

    <I>

    Account asq = new Account()

    StringWriter swQuery = new StringWriter();

    XmlSerializer xsQuery = new XmlSerializer(typeof(Account));

    xsQuery.Serialize(swQuery, asq);

    return swQuery.ToString();

    </I>

    Is there a better way to do this in order to avoid returning a string of Xml? I know the overhead is enormous and would love to know the answer. Thanks!

    Shaun McDonnell

    Citigroup

  16. Tim Huffam says:

    What about loose coupling?

    Bundling various parameters into an primitive type like a string (like the MSCRM APIs do)provides a simple way to acheive loose coupling does it not?

    Tim

  17. It’s not necessary at all to load a DOM to return well-formed XML from a webservice. And as the DOM will be the "legacy-deprecated-dying" API in .NET, it only makes sense to move to XmlReader and XPathNavigator-based processing.

    Here’s how you can return well-formed XML without wasting server resources in loading DOMs you don’t need: http://weblogs.asp.net/cazzu/archive/2004/05/31/144922.aspx (How to return well-formed XML from WebServices without the DOM: from XmlReader and XPathNavigator)

  18. yn says:

    Why go through the trouble of instantiating an XmlDocument, XmlReader or even an XPathNavigator if you already have the XML as a string to begin with and have no interest in interacting with it in that context?

    Let’s take Oracle’s (9i+) xmltype for example. It holds and returns XML as CLOB, which you normally cast to string. Assuming I simply want to forward this record from Oracle to my client, without interacting with it at all, why waste time and space over deserialization?

    In the client I can simply (assuming client is the browser in this sample and ‘result’ is callService’s callback argument):

    var xmlDoc=document.createElement("xml");

    xmlDoc.loadXML(result.value);

  19. Avoid returning XmlNode from a web service method at all costs.  In this post, I show why you should avoid returning XmlNode in web services, and demonstrate how to use the IXmlSerializable interface in .NET 2.0 web services for high performance XML serialization