(1 item) |
|
(1 item) |
|
(5 items) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(6 items) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(4 items) |
|
(2 items) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(2 items) |
|
(5 items) |
|
(3 items) |
|
(1 item) |
|
(1 item) |
|
(1 item) |
|
(3 items) |
|
(1 item) |
|
(1 item) |
|
(2 items) |
|
(8 items) |
|
(2 items) |
|
(7 items) |
|
(2 items) |
|
(2 items) |
|
(1 item) |
|
(2 items) |
|
(1 item) |
|
(2 items) |
|
(4 items) |
|
(1 item) |
|
(5 items) |
|
(1 item) |
|
(3 items) |
|
(2 items) |
|
(2 items) |
|
(8 items) |
|
(7 items) |
|
(3 items) |
|
(7 items) |
|
(6 items) |
|
(1 item) |
|
(2 items) |
|
(5 items) |
|
(5 items) |
|
(7 items) |
|
(3 items) |
|
(7 items) |
|
(16 items) |
|
(10 items) |
|
(27 items) |
|
(15 items) |
|
(15 items) |
|
(13 items) |
|
(16 items) |
|
(15 items) |
Matthew Adams emailed me to point out a useful technique that I didn't mention in my recent blog on service API evolution.
He noted that my somewhat contrived extensible schema makes life difficult for itself by putting the extensibility point right in the middle of the document. This is the place where it's most likely to trip up insufficiently robust clients. If you put it at the end, you are much less likely to catch out unwary clients. It should only break clients which actively check that you don't append any elements they weren't expecting.
Unfortunately, that's an easier mistake to make than you might think. Consider this code:
XmlReader rdr = GetXmlReaderFromSomewhere(); rdr.ReadStartElement("stuff"); string foo = rdr.ReadElementString("foo"); string bar = rdr.ReadElementString("bar"); rdr.ReadEndElement(); // Danger!
This expects to see documents of the form:
<stuff> <foo>Hello</foo> <bar>World</bar> </stuff>
Unfortunately, it will fail even if you add new elements after the bar
. The problem is that
final line of code where it calls ReadEndElement
. The semantics of that call are essentially 'I expect
the very next content item in the document to be the end of the stuff
element, and I want you
to throw an exception if that's not the case'. So if you give it this instance:
<stuff> <foo>Hello</foo> <bar>World</bar> <unexpected>The Spanish Inquisition</unexpected> </stuff>
the call to ReadEndElement
will throw an XmlException
complaining that it wasn't
expecting to see another element at this point in the document. It's not all that easy to make it robust either - calling
Skip
doesn't work, because the cursor is not in the right place after the call to
ReadElementString
.
This is why I prefer to use either XPath, or XmlSerializer
- both are much more robust in the face of
these kinds of changes. XmlReader
is a fairly low level API. It's efficient, being a streaming API, but
it's not easy to make it work with changing schemas. The higher level XPath and XmlSerializer
APIs
are definitely the way to go if you want to be well placed to cope with change.
ObDreadfulPun: I was going to call this entry 'Tails (sic) of the Unexpected' but I'm not sure if that program was broadcast outside of the UK...