I am trying to implement an XML Schema which will enforce the following the XML ;
<databases>
<database>
<name>"Test A"</name>
<host>"192.168.0.100"</host>
<default>yes</default>
</database>
<database>
<name>"Test B"</name>
<host>"192.168.0.200"</host>
<default>no</default>
</database>
<database>
<name>"Test C"</name>
<host>"localhost"</host>
<default>no</default>
</database>
</databases>
I am able to implement the XML Schema myself except for one crucial issue; and that is that a maximum of only one database should be marked as the default. This means that zero databases could be marked as the default and this should also be considered valid.
As an example, the following XML should be considered invalid by the XML Schema, since more than one database is marked as default.
<databases>
<database>
<name>"Test A"</name>
<host>"192.168.0.100"</host>
<default>yes</default>
</database>
<database>
<name>"Test B"</name>
<host>"192.168.0.200"</host>
<default>no</default>
</database>
<database>
<name>"Test C"</name>
<host>"localhost"</host>
<default>yes</default>
</database>
Whereas the following XML should be considered valid by the XML Schema since no (zero) databases are marked as default ;
<databases>
<database>
<name>"Test A"</name>
<host>"192.168.0.100"</host>
<default>no</default>
</database>
<database>
<name>"Test B"</name>
<host>"192.168.0.200"</host>
<default>no</default>
</database>
<database>
<name>"Test C"</name>
<host>"localhost"</host>
<default>no</default>
</database>
Does anyone know if it is possible to enforce such a constraint with XML Schemas? I feel as though it should be, but I’m not sure how to go about implementing it.
Any assistance with respect to this matter would be immensely appreciated.
Thanks in advance.
The easiest way to check that only one database is marked as the default is probably to structure your XML differently: record the databases as above, but drop the
defaultelement, and add an attribute or child element to thedatabaseselement which identifies the default database. Your XML becomes:(I have dropped the quotation marks in the
nameelements, on the theory that they are not actually part of the database name. If they are part of the name, then the default attribute will need to be something likedefault='"Test A"', including the quotes.)You will need to ensure that the
database/nameelement is unique; do this with anxs:keyconstruct in the declaration of thedatabaseselement. You will also need to ensure that the optionaldefaultattribute points at a database name; do this withxs:keyref. The declaration ofdatabasesmight look something like this:A second approach makes a slightly devious use of
xs:unique, but again requires refactoring your XML. Instead of making the third child ofdatabasebedefaultwith valuesyesorno, make the third child be either an element nameddefault-databaseor an element namednon-default-database(change the names to suit your preferences, of course). Define these in such a way as to ensure that every instance ofdefault-databasehas the same simple-type value.Then specify that no two occurrences of
default-databasemay have the same string value, usingxs:unique:Since every instance of
default-databasehas the same value, this uniqueness constraint ensures that there can only ever be one such element. The schema as a whole might look something like this. First the bookkeeping:Now the element declaration for
databases:And its complex type:
Now some more bookkeeping: declaration for
database, just to keep us honest.And a final bit of bookkeeping: a type that ensures that every instance of
default-databasehas the same value. (There are other ways to achieve this, of course; this is just the one that came to mind first.)If you are absolutely married to your existing design, then I don’t know how to do what you want to do in XSD 1.0; if you can use XSD 1.1, of course, you can add an assertion on
databasesthat saysBut as the alternative solutions above illustrate, that design and the use of assertions is not your only option.