Tuesday, April 18, 2006

Overkill? I think not

It's good to see Tim blogging again. Hopefully he'll keep it up this time (nudge, nudge, Tim). At any rate, I'll blame his lack of practice for his post "Craig urges overkill, XmlSerializer sky not falling". Either that, or he had a high temperature/blood alcohol level. :)

 

Tim writes:

 

Craig got caught in a very particular set of circumstances. First, he started with WSDL. Then he hand-wrote his serializable types. Then he followed his preferred set of rules for ordering members of those types alphabetically.

 

The implication here is that unless you're doing all those things, you won't have the issues I describe in my post. This is simply not true. The problem I describe is an issue for anyone who does not explicitly control the order of serialization of their web service-visible types. Period. While I wouldn't exactly say the sky is falling, this is definitely a Big Deal.

 

I think the reason you don't see this occurring as a problem more often in the wild is that people tend to write .NET clients for their .NET web services, and XmlSerializer doesn't care about order. Or, more generally, schema validity. But if reach is important to your web service, you should.

 

I actually talked with Tim about this on the phone, and it came out during the conversation that the problem is even worse than I first thought. I had detected the issue with return types, but the fact of the matter is that if you reorder your type members for either input or output parameters, you change the generated schema in the WSDL. And that's a breaking change (from a schema standpoint).

 

Which really was my whole point: you need to be EXCEEDINGLY careful with your types unless you implement IXmlSerializable or use XmlElementAttribute.Order. Reordering type members is just too easy for a developer to do without thinking about it, and isn't the sort of thing that's easy to catch as critical even if your team reviews all source changes.

 

As Tim points out, implementing the read side of IXmlSerializable is a royal pain most of the time (you'll note I only said "consider" using it), but XmlElementAttribute.Order is pretty easy. Of course, it's only available in .NET 2.0. :p

5 comments:

  1. I implemented a server-side specification for EPC/RFID called ALE and it includes a WSDL/XSD for the service communication mechanism. I used their WSDL/XSD (made by Java guys in a Java-friendly manner) and used wsdl.exe to generated the server side stubs and serializable classes (.NET 1.1) and the element ordering was a huge sticking point.



    No matter what order I put the properties on my types in they wouldn't serialize in the correct order (it used sequence instead of any). I considered IXmlSerializable at the time but .NET 2.0 hadn't beta'd yet and it is marked as do not use in 1.1 and we didn't want to risk having to redo our implementation so we opted for using one of the SoapReceiver classes in WSE and format the messages ourselves.



    Your previous article focuses on return types but never focuses on input types, which I think is much more of a PITA. Using an XmlWriter is exceedingly easy, but using an XmlReader is way more of a pain than it's worth.

    ReplyDelete
  2. Agreed. As I said, I first encountered it with respect to return types, but input types have exactly the same problem. And you're right again when you point out that the read part of XmlSerializer is much more painful than the write part.



    One trick I considered implementing was to use an XmlSerializable nested class for the read, calling Deserialize on it in my implementation of IXmlSerializable.ReadXml, then just copying out any relevant data. Much easier than building the stupid state machine you need with XmlReader.

    ReplyDelete
  3. That would be an annoying duplication of a ton of classes/properties in my case, but definitely an option. The only thing I'm curious about is how it would all work with Bare-style element passing:



    On the class itself:

    [System.Web.Services.Protocols.SoapDocumentService( RoutingStyle=SoapServiceRoutingStyle.RequestElement )]



    On the method:

    [System.Web.Services.Protocols.SoapDocumentMethodAttribute( Use=System.Web.Services.Description.SoapBindingUse.Literal, ParameterStyle=System.Web.Services.Protocols.SoapParameterStyle.Bare)]



    These are both required for the service I implemented. The routing style because the WSDL does not specify a SOAPAction, and Bare parameter style because there's no need to wrap the parameters in a wrapper Request object since the WSDL already defines that. Then consider that I have the same type used as the parameter for multiple methods and you end up with multiple root element names defining the same type.



    It's been so long I've had to work with it, I don't remember how the XmlSerializer handles that. Three types or just keep the XmlType/XmlRoot attribute off the type or will it just read the root element and assume you know what you're doing?

    ReplyDelete
  4. Adam,



    Can you provide more information on the problem you had with ordering? I've just never seen this in the wild, so I'm curious.



    The XmlSerializer issue is only related to doc/lit/bare services in as much as you're using the serializer to create the "verb" wrapper element (if you have one).



    Thanks,

    Tim-

    ReplyDelete
  5. The ordering is the same problem that Craig is talking about. The WSDL/XSD specify an xsd:sequence instead of xsd:any and the XmlSerializer won't order them correctly when the types were generated with wsdl.exe.



    My point about Bare parameter style is only in regard to Craig's previous comment on using a serializable nested class, and I was point out that I wasn't sure what would happen to the XmlSerializer if you give it a single type but send an XmlReader at different element names (so multiple root element names can represent the same type).

    ReplyDelete