Elements containing either text or other elements

Overview
A question pops up now and again in XML-related newsgroups about being able to define an element that could contain either text or other elements.  Basically, people want to say

<myelement>
Some text
</myelement>

Or

<myelement>
<child1>1</child1>
<child2>2</child2>
</myelement>

A simple answer to this question is no.  If this is exactly what you want, you can’t do it.  However, there are several ways to get similar results.  Below I outline some of them.
 

Method 1: mixed type

First of all, you can define <myelement> to be of mixed type:

<xs:complexType name ="myelement" mixed="true">
<xs:sequence minOccurs="0">
<xs:element name="child1" type="xs:integer" />
<xs:element name="child2" type="xs:integer" />
</xs:sequence>
</xs:complexType>

<xs:element name="myelement" type="myelement" />

This is a simple solution that allows you to have the two cases outlined earlier.  However, with a mixed model you cannot constrain the position of the text nodes.  So if you use it, it will also allow both text AND <child> elements where the text can appear before and/or after and/or in between <child> elements as in the following example:

 <myelement>
Some text
<child1>3</child1>
Some more text
<child2>4</child2>
Some more text
</myelement>

Method 2: using xs:anyType
Another approach is to define a separate type that takes <child1> and <child2> elements and define <myelement> to be of type xs:anyType:

<xs:complexType name ="myelementWithChildren">
<xs:sequence>
<xs:element name="child1" type="xs:integer" />
<xs:element name="child2" type="xs:integer" />
</xs:sequence>
</xs:complexType>

<xs:element name="myelement" type="xs:anyType" />

Then you can use xsi:type attribute in the instance document to specify which type to use:

<myelement>
Some text
</myelement>

Or

<myelement

  xmlns:xsi=https://www.w3.org/2001/XMLSchema-instance
xsi:type="myElementWithChildren">
<child1>3</child1>
<child2>4</child2>
</myelement>

The drawback of this approach is that it allows the use of any type within <myelement>.  For example,

<myelement xmlns:xsi=https://www.w3.org/2001/XMLSchema-instance
xsi:type="myPhoneElement">
<number>1234567890</number>
<ext>21</ext>
</myelement>

Method 3: using an abstract type

If the first two approaches are not acceptable because you really want to limit the content of <myelement> to either text or a specific type, here’s a slightly more complicated way to achieve it.  First, we need to define a base type that includes both content models.  We will also make this type “abstract” to prevent the use of text and <child> elements at the same time:

<xs:complexType name ="myElementBase" mixed="true" abstract="true">
<xs:sequence minOccurs="0">
<xs:element name="child1" type="xs:integer" />
<xs:element name="child2" type="xs:integer" />
</xs:sequence>
</xs:complexType>

This type accepts <child> elements (sequence) and text (mixed=”true”).  Next, we will derive by restriction another type that will accept just the <child> elements and no text (this is done by setting mixed to “false”):

<xs:complexType name="myElementNoText" mixed="false">
<xs:complexContent>
<xs:restriction base="myElementBase">
<xs:sequence>
<xs:element name="child1" type="xs:integer" />
<xs:element name="child2" type="xs:integer" />
</xs:sequence>
</xs:restriction>
</xs:complexContent>
</xs:complexType>

Next we need to derive by restriction another class that only accepts text and no <child> elements:

<xs:complexType name="myElementText" mixed="true">
<xs:complexContent>
<xs:restriction base="myElementBase" />
</xs:complexContent>
</xs:complexType>

Now we define <myelement> element to be of type myElementBase:

<xs:element name="myelement" type="myElementBase" />

Finally, we need to specify which derived type we want to use.  This is done inside the instance documents:

<myelement
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
  xsi:type="myElementNoText">
<child1>3</child1>
<child2>4</child2>
</myelement>

Or

<myelement
xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
xsi:type="myElementText">
Some text
</myelement>

This method provides a better solution to the original problem than the other two.  It allows the use of either text or a specific type and prohibits the use of anything else within <myelement>.  However, it does require you to use the xsi namespace.

Final Words
Two of the three methods that I’ve described here take advantage of the xsi namespace and it wouldn’t be fair if I didn’t mention its drawbacks.  There is a belief that including schema features, such as xsi:type, inside instance documents removes one of the main advantages of XML – decoupling of data.  As far as I know, there is no clear consensus about this issue.  So my advice would be, feel free to use the xsi namespace if you can’t avoid it.

Stan Kitsis

Program Manager, XML Tools