This weeks XML-Deviant article looks at the problem of unique identifiers in XML documents, a loop-hole in XPointer, and proposals on XML-DEV to resolve the issue.
Understanding the ID
The high level of activity on XML-DEV over the last few weeks has been sustained with a recent flurry of emails being exchanged on the topic of unique identifiers within XML documents. The issue surfaced following a seemingly straightforward problem statement from Fabio Dianda: how does you identify ID attributes without a DTD or when using a non-validating parser? The problem seems straightforward because the answer is that you can't. The only way to discover if an attribute is an ID is by using a schema, or if your application is hard-coded with its name. Game over.
However for several people, including Tim Bray and James Clark, this isn't an acceptable situation. In a posting to a related thread on an IETF mailing list, Bray described it as
...maybe the #1 gaping architectural hole as regards XML & the Web. The problem is that at the moment, given some arbitrary XML, there is *no* good way to determine what's an ID without recourse to some external resource like a DTD or schema, and that, to use a technical term, sucks.
In a followup post to XML-DEV Bray elaborated on the primary use case for ID attribute discovery.
It's really important for the workings of the web that an address such as
have well-defined semantics. If foo turns out to be XML, this is hopelessly underdefined.
Fragment identifiers, like
#Chapter12, are defined in
RFC 2396, which
explains that the precise meaning of fragment identifier is dependent
on the media type of the data being retrieved by the URI. For a Web
page this has been defined such that a fragment identifier refers to
an HTML named anchor,
<a name="Chapter12"/>. For an XML document the
meaning of a fragment
identifier is described in the XPointer Candidate
Recommendation, using what is
known as the Bare Name
syntax. Under these
processing rules, our fragment identifier would be interpreted as the
following XPath expression:
Following the trail of specifications a step further we discover that the XPath id function selects elements by their unique ID attribute, as declared in a DTD. Although, slightly confusingly, it makes no assertions about whether the document is actually valid, so in practice that identifier might not actually be unique: XPath treats any subsequent elements with the same identifier as not having an identifier at all.
To summarize, then, one can only link to an element within an arbitrary XML document using a bare XPointer if the element has an ID attribute and if the document has a DTD. Thus, XPointer doesn't play well with simple well-formed documents, arguably one of XML's greatest gifts.
Of course one could use an XPointer that explicitly identifies the required element, e.g. using a child sequence. But this is a more fragile solution particularly in the face of document edits. There are also benefits to having a uniform syntax for fragment identifiers; for, as David Carlisle observed, if
#foo has a meaning for text/html it would be nice if you could arrange that the same uri-reference located a more or less equivalent thing if something of type application/xml was returned. If the fragment ID syntax for application/xml is different...then you can't have a single uri reference that works regardless of the mime type returned.
Judging by the lengthy discussion that ensued, there is agreement that this issue needs to be resolved. But the exact extent of the problem, and the best way to fix it, are still under discussion.
Gaining an Identity
While the primary use case presented so far as been the need to link to portions of XML documents on the Web, the scope of the problem actually seems wider. CSS allows styling to be applied to individual elements, using id selectors. Acknowledging that this feature is problematic without a DTD (or guarantee that a user agent may read the DTD), the CSS authors ended up recommending a work-around to avoid the problem altogether. If a generic means of attaching an identifier to an element without using a DTD were available, then this uncertainty could be resolved. The same applies to other specifications as Michael Champion noted.
It's not just one spec, id-ness is exposed to users of XPath, XPointer, XSLT, XLink, and DOM. More importantly, it's a widely used feature in real applications that use these specs, especially when an XML app is working with a database. I use "getElementById()" whenever I can (e.g., I control the XHTML and can put in the necessary attributes).
I agree with Tim Bray: this is a "gaping architectural hole" because these other specs don't require DTDs or schemas in the general case, and have to mumble to describe what is supposed to happen if there isn't a DTD/Schema to define id-ness. None care (at their core) about the other features that DTD/Schema brings to the table, they just need a way to define id-ness.
Most of the proposals suggested so far have targeted a general solution, which I briefly summarize here.
Use the Internal Subset
This proposal sidesteps the issue by requiring that ID attributes be declared using the DTD internal subset. This hasn't been generally popular, mainly because of the overheads it imposes on document authors, particularly for languages such as MathML than have many attributes with identifiers.
One of the most recent proposals has been from Don Park. In this scenario, ny attribute called "id" should be an ID. This idea is simple, and there is some indication that it was a common practice in SGML circles. But it wouldn't handle vocabularies that had identifiers with alternative names.
Use a Processing Instruction
This proposal suggests using a Processing Instruction (PI) that lists the attributes that should be interpreted as identifiers. While this seems to have low impact -- it has little effect on validity -- many oppose PIs in principle, despite persistent claims, by Rob Lugt, Marcus Carr and others, that this is the most sensible option.
<?xml-typeinfo idnames="x"?> <foo > <bar x="abc"/> <baz x="hij" /> </foo>
Pages: 1, 2