Схема XML: корневой элемент
Следующий пост спрашивает, как указать, что элемент является корневым элементом в схеме XML:
Можно ли определить корневой элемент в документе XML с использованием схемы?
Я следовал руководству w3schools по XML-схеме, но что-то все еще не ясно. Рассмотрим пример схемы 2 с http://www.w3schools.com/schema/schema_example.asp(воспроизведен ниже для удобства). Как этот код указывает, что <shiporder>
это корневой элемент? Разве в примере не сказано, что все элементы действительны как корневые элементы?
------------------ пример ------------------------------- ---
<?xml version="1.0" encoding="ISO-8859-1"?>
<shiporder orderid="889923"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="shiporder.xsd">
<orderperson>John Smith</orderperson>
<shipto>
<name>Ola Nordmann</name>
<address>Langgt 23</address>
<city>4000 Stavanger</city>
<country>Norway</country>
</shipto>
<item>
<title>Empire Burlesque</title>
<note>Special Edition</note>
<quantity>1</quantity>
<price>10.90</price>
</item>
<item>
<title>Hide your heart</title>
<quantity>1</xample saying that all elements are valid as root elements?quantity>
<price>9.90</price>
</item>
</shiporder>
----------------------- схема ------------------------
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema">
<!-- definition of simple elements -->
<xs:element name="orderperson" type="xs:string"/>
<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:element name="title" type="xs:string"/>
<xs:element name="note" type="xs:string"/>
<xs:element name="quantity" type="xs:positiveInteger"/>
<xs:element name="price" type="xs:decimal"/>
<!-- definition of attributes -->
<xs:attribute name="orderid" type="xs:string"/>
<!-- definition of complex elements -->
<xs:element name="shipto">
<xs:complexType>
<xs:sequence>
<xs:element ref="name"/>
<xs:element ref="address"/>
<xs:element ref="city"/>
<xs:element ref="country"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="item">
<xs:complexType>
<xs:sequence>
<xs:element ref="title"/>
<xs:element ref="note" minOccurs="0"/>
<xs:element ref="quantity"/>
<xs:element ref="price"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="shiporder">
<xs:complexType>
<xs:sequence>
<xs:element ref="orderperson"/>
<xs:element ref="shipto"/>
<xs:element ref="item" maxOccurs="unbounded"/>
</xs:sequence>
<xs:attribute ref="orderid" use="required"/>
</xs:complexType>
</xs:element>
</xs:schema>
С моей точки зрения, XML-схема должна делать две вещи:
- определить, что может происходить внутри каждого узла
- определить, где каждый узел может быть размещен
И кажется, что пример не на втором месте. Какие-либо предложения?
6 ответов
Насколько я знаю, любой глобально определенный элемент может быть использован в качестве корневого элемента, и XML-схема не имеет понятия для указания того, каким должен быть корневой элемент.
Однако вы можете обойти эту проблему, хорошо спроектировав свою XML-схему, чтобы в ней был только один глобально определенный элемент - тогда только этот элемент действителен в качестве корневого элемента.
Пример этого можно найти в W3Schools (заголовок " Использование именованных типов"). Этот пример имеет только один глобально определенный элемент и, следовательно, только один возможный корневой элемент.
Не все согласны с этим, но тот факт, что XML-схема не может указать корневой элемент, является задуманностью. Смысл в том, что если <invoice>
действителен, когда это единственное в документе, тогда он одинаково действителен, если он содержится в чем-то еще. Идея состоит в том, что контент должен быть многократно используемым, и вам нельзя позволять запрещать кому-либо использовать действительный контент как часть чего-то большего.
(Тот факт, что ID и IDREF относятся к документу, скорее противоречит этой политике; но тогда язык был разработан довольно большим комитетом.)
Да ты прав. XSD должен быть:
<?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>
как видите, сейчас есть только один xs:element
и этот единственный является допустимым корневым элементом:)
Как этот код указывает, что является корневым элементом?
Джон, эта схема только что определила все элементы, и любой из них может быть выбран в качестве корневого элемента. Если вы попытаетесь сгенерировать образец xml из любого инструмента, такого как Altova XML Spy или его аналога, вы сможете выбрать элемент, который будет корневым элементом.
Таким образом, любой из этих элементов может быть корнем.
Чтобы избежать неоднозначности, используйте один глобально определенный элемент.
Недостатком большого количества глобальных элементов является то, что все они могут использоваться в качестве корневых элементов для документов. Преимущество заключается в том, что вы можете использовать элемент при определении новых типов, которые обеспечат совпадение пространства имен дочерних элементов с именами родительского типа.
Я перестал думать, что должен быть только один глобальный элемент, и что все сложные типы должны иметь глобальный элемент.
На основе приведенного вами примера можно найти единственный корневой элемент.
Вы можете получить список глобальных элементов, а затем получить список вложенных элементов, на которые ссылается complexType в узле xs:sequence, таким образом, корневой элемент находится в списке глобальных элементов, но не в списке вложенных элементов.
Я сделал это с помощью класса XmlSchemaSet в.NET. Вот фрагмент кода:
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));
Метод расширения 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;
}
}
}
}
Но все еще есть проблемы для общего xsd при использовании этого подхода. Например, в DotNetConfig.xsd, который Visual Studio использует для файла конфигурации, корневой элемент определяется следующим образом:
<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>
Я еще не нашел полного решения для работы со всеми видами схем. Будем продолжать за это.