The following post asks how to indicate that an element is the root element in an XML schema:
Is it possible to define a root element in an XML Document using Schema
The disadvantage of lots of global elements is they could all be used as root elements for documents. The advantage is then you can use the element when defining new types which will assure the namespace of the child elements match those of the parent type.
I have changed from thinking there should only be one global element to that all complex types should have a global element.
As far as I know, any globally defined element can be used as root element, and XML Schema does not have a notion for specifying what the root element is supposed to be.
You can however work around this by designing your XML Schema well, so that there is only one globally defined element - then only this element is valid as root element.
An example of this can be found at W3Schools (heading Using Named Types) This example only has one globally defined element, and thus only one possible root element.
How does this code indicate that is the root element?
John, That schema just defined all the elements and any of those can be chosen as a root element. If you try generating a sample xml from any tool like Altova XML Spy or its kind, you will get to choose an element to be the root element.
So any of those elements can be the root.
To prevent ambiguity, use one globally defined element.
Not everyone agrees with it, but the fact that XML Schema can't specify a root element is by design. The thinking is that if an <invoice>
is valid when it's the only thing in a document, then it is equally valid if it is contained in something else. The idea is that content should be reusable, and you shouldn't be allowed to prevent someone using valid content as part of something larger.
(The fact that ID and IDREF are scoped to a document rather goes against this policy; but then the language was designed by a rather large committee.)
yes, you are right. the xsd should be:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- definition of attributes -->
<xs:attribute name="orderid" type="xs:string"/>
<!-- definition of complex elements -->
<xs:complexType name="shiptoType">
<xs:sequence>
<xs:element name="name" type="xs:string" />
<xs:element name="address" type="xs:string" />
<xs:element name="city" type="xs:string" />
<xs:element name="country" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:complexType name="itemType">
<xs:sequence>
<xs:element name="title" type="xs:string" />
<xs:element name="note" minOccurs="0" type="xs:string" />
<xs:element name="quantity" type="xs:string" />
<xs:element name="price" type="xs:string" />
</xs:sequence>
</xs:complexType>
<xs:element name="shiporder">
<xs:complexType>
<xs:sequence>
<xs:element name="orderperson" type="xs:string" />
<xs:element name="shipto" type="shiptoType"/>
<xs:element name="item" maxOccurs="unbounded" type="itemType"/>
</xs:sequence>
<xs:attribute ref="orderid" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>
as you see, now there is only one xs:element
, and that one is the only one that can be a valid root element :)
Based on the example that you provided, it is possible to find the only root element.
You can get a list of global elements, then get a list a nested elements that referenced in complexType under the node xs:sequence, thus the root element is the one in global elements list but not in nested elements list.
I have done this by using XmlSchemaSet class in .NET. Here is the code snippet:
var localSchema = schemaSet.Schemas().OfType<XmlSchema>().Where(x => !x.SourceUri.StartsWith("http")).ToList();
var globalComplexTypes = localSchema
.SelectMany(x => x.Elements.Values.OfType<XmlSchemaElement>())
.Where(x => x.ElementSchemaType is XmlSchemaComplexType)
.ToList();
var nestedTypes = globalComplexTypes.Select(x => x.ElementSchemaType)
.OfType<XmlSchemaComplexType>()
.Select(x => x.ContentTypeParticle)
.OfType<XmlSchemaGroupBase>()
.SelectMany(x => x.GetNestedTypes())
.ToList();
var rootElement= globalComplexTypes.Single(x => !nestedTypes.Select(y => y.ElementSchemaType.QualifiedName).Contains(x.SchemaTypeName));
The extension method GetNestedTypes:
static IEnumerable<XmlSchemaElement> GetNestedTypes(this XmlSchemaGroupBase xmlSchemaGroupBase)
{
if (xmlSchemaGroupBase != null)
{
foreach (var xmlSchemaObject in xmlSchemaGroupBase.Items)
{
var element = xmlSchemaObject as XmlSchemaElement;
if (element != null)
yield return element;
else
{
var group = xmlSchemaObject as XmlSchemaGroupBase;
if (group != null)
foreach (var item in group.GetNestedTypes())
yield return item;
}
}
}
}
But there still has problems for the general xsd when using this approach. For example, in DotNetConfig.xsd that Visual studio use for configuration file, the root element is define as below:
<xs:element name="configuration">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:any namespace="##any" processContents="lax" />
</xs:choice>
<xs:anyAttribute namespace="http://schemas.microsoft.com/XML-Document-Transform" processContents="strict"/>
</xs:complexType>
</xs:element>
I havn't found a complete solution to deal with all kinds of schemas yet. Will continue for it.