0

My new XML private language includes elements <figure>, representing illustrations (image + caption).

Whenever illustrations refer to some image in the local database I just want to type

<figure id="9809" width="full" />

to identify image number 9809 and its associated caption.

On the other side, if images come from outside I will need a slightly different syntax:

<figure href="https://some-url-here" width="full">Some ad hoc catpion</figure>

So far I have declared an element that combine both behaviours, like this:

  <!-- Figures -->
  <xs:simpleType name="FigureWidthEnum">
    <xs:restriction base="xs:token">
      <xs:enumeration value="center" />
      <xs:enumeration value="half" />
      <xs:enumeration value="full" />
    </xs:restriction>
  </xs:simpleType>

  <xs:element name="figure">
    <xs:complexType mixed="true">
      <xs:complexContent>
        <xs:extension base="Inline">
          <xs:attribute name="href" type="URI" />
          <xs:attribute name="id" type="xs:nonNegativeInteger" />
          <xs:attribute name="width" type="FigureWidthEnum" default="full" />
        </xs:extension>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>

It works fine but a fresh editor can mess with the 3 attributes and type impossible things I don't want to pass the Schema Validator that easy. For example:

<figure id="9809" width="full" href="https://some-unexpected-url">Some unexpected caption that should not be here</figure>

I want to have two completely separate sintaxes for <figure>, as if I could declare these two elements with the same name:

  <xs:element name="figure">
    <xs:complexType mixed="true">
      <xs:complexContent>
        <xs:extension base="Inline">
          <xs:attribute name="href" type="URI" />
          <xs:attribute name="width" type="FigureWidthEnum" default="full" />
        </xs:extension>
      </xs:complexContent>
    </xs:complexType>
  </xs:element>

  <xs:element name="figure">
    <xs:complexType>
      <xs:attribute name="id" type="xs:nonNegativeInteger" />
      <xs:attribute name="width" type="FigureWidthEnum" default="full" />
    </xs:complexType>
  </xs:element>

In fact it is not possible.

Can it be done somehow?

coterobarros
  • 941
  • 1
  • 16
  • 25

1 Answers1

2

Yes, it is possible with XSD 1.1, which provides <xs:alternative> element intended specifically for such requirements. Here's a complete XML schema I've designed to validate exactly as you need:

<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
           xmlns:vc="http://www.w3.org/2007/XMLSchema-versioning"
           vc:minVersion="1.1">

  <!-- Figures -->
  <xs:simpleType name="FigureWidthEnum">
  <xs:restriction base="xs:token">
    <xs:enumeration value="center" />
      <xs:enumeration value="half" />
      <xs:enumeration value="full" />
    </xs:restriction>
  </xs:simpleType>

  <!-- stub definition -->
  <xs:complexType name="Inline"/>

  <!-- 'figure' element is declared once, but its type has two alternatives -->
  <xs:element name="figure">

    <!-- The first alternative is selected, when the condition specified
     in 'test' attribute is true. The condition must be a boolean XPath
     expression. The '@id' returns the value of 'id' attribute. However,
     when converted to boolean, it indicates whether 'id' attribute is
     specified at all. The anonymous complexType inside <xs:alternative>
     provides the element type valid for this case.
    -->
    <xs:alternative test="@id">
      <xs:complexType>
        <xs:attribute name="id" type="xs:nonNegativeInteger" />
        <xs:attribute name="width" type="FigureWidthEnum" default="full" />
      </xs:complexType>
    </xs:alternative>

    <!-- The second alternative has no 'test' attribute. That means, it must
     be selected by default, when all other alternatives (with a specified
     test condition) do not pass. Here, the anonymous complexType inside
     <xs:alternative> defines the element type in case of reference:
     when 'href' is present.
    -->
    <xs:alternative>
      <xs:complexType mixed="true">
        <xs:complexContent>
          <xs:extension base="Inline">
            <xs:attribute name="href" type="xs:anyURI" />
            <xs:attribute name="width" type="FigureWidthEnum" default="full" />
          </xs:extension>
        </xs:complexContent>
      </xs:complexType>
    </xs:alternative>
  </xs:element>

  <!-- this element is defined just to test the whole schema in XML below -->
  <xs:element name="figures">
    <xs:complexType>
      <xs:sequence>    
        <xs:element ref="figure" minOccurs="0" maxOccurs="unbounded"/>
      </xs:sequence>    
    </xs:complexType>
  </xs:element>

</xs:schema>

A complete XML file this schema validates:

<?xml version="1.0" encoding="UTF-8"?>
<figures>

  <!-- passes validation -->
  <figure id="9809" width="full" />

  <!-- passes validation -->
  <figure width="full" href="https://some-unexpected-url">Some ad hoc caption</figure>

  <!-- does not pass the validation -->
  <figure id="9809" width="full" href="https://some-unexpected-url">
    Some unexpected caption that should not be here
  </figure>

</figures>

The validation was done with Apache Xerces 2.11.0 (XSD 1.1 aware).


Promo add-on. These links may be interesting for those working with XML schemas and WSDL: FlexDoc/XML XSDDoc & WSDLDoc – High-performance universal XML Schema / WSDL Documentation Generators with Diagrams

ColdFusion
  • 2,381
  • 13
  • 14
  • Wow @ColdFusion ! Thank you so much. I had tried with with no success because I didn't know about . – coterobarros Feb 11 '19 at 15:04
  • Unfortunately, upgrading from XSD 1.0 to XSD 1.1. requires different validation Java classes (I am using java.xml). I tested your chunk of XSD and is not recognized at all. For the benefit of readers implementing this solution in Java I've learnt that Xerces must be used instead. Take a look at https://stackoverflow.com/questions/20807066/how-to-validate-xml-against-xsd-1-1-in-java – coterobarros Feb 11 '19 at 17:12