Craig mentioned recently that he would never use the [XmlInclude] attribute because he felt it was impolite to send someone a SOAP message that used the xsi:type attribute. Kevin commented, asking why he felt that way and what toolkits might have a problem with it. I feel compelled to comment...
XML 1.0 + Namespaces do not define the notion of typed data. Everyone agrees that we need a way to assign simple types to the text value of an attribute or an element, e.g., published is a date and age is an integer. XSD adds this functionality, but goes much much further. It defines the notion of complex type, complex type derivation and complex type substitutability (at both the type and element level).
It is easy for OO developers to see a strong parallel between XSD complex types and classes, and XML instances and objects. Complex derivation by extension parallels classic OO inheritance nicely, with xsi:type providing the necessary run-time type information to indicate when a derived type is taking the place of a base type.
Appealing as this appears to be, there are two key differences between the OO and XML world that can't be ignored.
First, in modern OO systems like the CLR and Java, we always have type information at dev time and at runtime. You never have an object without also having it's type. This is not true for XSD. There is no standard way to locate and load the XSD for a given instance. Even if there were, most plumbing wouldn't do it because of the overhead. So instead, we have tools that we manually point at a schema and they consume it at dev time, but at runtime the XSD isn't there. So we don't really have XML run-time type information (unless we explicitly load it ourselves, which we rarely do); we have a projection of that information into CLR or Java types, where the mechanics of the projection are defined by our marshaler.
Second, and more important, inheritance and substitution mean very different things in OO and XSD. OO inheritance and substitution is based on behavioral polymorphism. A base type defines a behavioral contract that a consumer adheres to. If the consumer is given an instance of a more derived type, the consumer doesn't have to change. In contrast, XSD inheritance and substitution (both type and element based) provide structural polymorphism. All XML consumers analyze document structure in one way or another (stream API, tree API, XPath query, XSLT, etc.). If a consumer is written to process instances of a base type, what happens when it is given an instance of a derived type? The consumer has to be modified to check for substitution and react accordingly. That means looking for xsi:type on every element and for alternate elements in the substitution group of the element they were expecting. No one working in raw XML writes code that works that way. They write code that says “is the next element {www.example.org}person“, not “is the next element {www.example.org}person or something that a schema says is legally substitutable for that“.
Now, clearly you can make xsi:type-based substitution work with today's toolkits, so is all this just a point of theoretical purity?
No.
If you leverage this mechanism you are forcing a particular model on the person at the other end of the wire. Specifically, you are making it extremely difficult to process messages as XML directly. If they do, chances are they'll miss what you are doing. Best case, they fail. Worse case, the appear to succeed, but the results are odd because they didn't really understand what you were doing.
If you look closely at the Web services specifications, you'll see that none of them are defined in terms of XSD complex types, let alone type substitution. The SOAP 1.2 spec only mentions type relative to individual simple text values in elements or attributes like mustUnderstand and faultcode. You won't find complex type definitions in the WS-* specs either. Instead, they define things using a simpler psuedo-schema that focuses on element structure. The WS-Addressing spec does define the EndpointReference type, but the definition is by XML sample instance not XSD complex type. Yes, there are XML schemas for the WS specs, but they are explicitly non-normative. Next to anything the specs themselves, the schemas don't mean anything.
So guess what happens if you send SOAP messages with envelopes or WS-* headers where you've replaced the expected types or elements with something else? The answer is no one knows because Web service stacks are implement using XML and don't apply the same rules to themselves that they apply to the data they carry.
I don't know about you, but the thought of taking an approach to modeling the payload of my messages that is radically different than the approach taken to model the message itself makes me really nervous. So I stick much more closely to how XML itself works. That means focusing on composition and wildcards instead of inheritance and substitution. In fact, I rarely publish a schema that is not explicitly marked blockDefault=”#all” to simply disable type and element substitution altogether.
Posted
Jul 12 2004, 07:48 PM
by
tim-ewald