development

XML 스키마 : 루트 요소

big-blog 2021. 1. 7. 20:36
반응형

XML 스키마 : 루트 요소


다음 게시물에서는 요소가 XML 스키마의 루트 요소임을 나타내는 방법을 묻습니다.

스키마를 사용하여 XML 문서에서 루트 요소를 정의 할 수 있습니까?

XML 스키마에 대한 w3schools 자습서를 따랐지만 아직 명확하지 않습니다. http://www.w3schools.com/schema/schema_example.asp의 예제 스키마 2를 고려 하십시오 (편의를 위해 아래에 복제 됨). 이 코드는 그것이 <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 스키마는 두 가지 작업을 수행해야합니다.

  1. 각 노드 내에서 발생할 수있는 일 정의
  2. 각 노드를 배치 할 수있는 위치 정의

그리고 예제는 # 2에서 실패한 것 같습니다. 제안 사항이 있습니까?


내가 아는 한 전역 적으로 정의 된 요소는 루트 요소로 사용할 수 있으며 XML 스키마에는 루트 요소가 무엇인지 지정하는 개념이 없습니다.

그러나 XML 스키마를 잘 설계하여이 문제를 해결할 수 있으므로 전역 적으로 정의 된 요소가 하나만 있습니다. 그러면이 요소 만 루트 요소로 유효합니다.

이에 대한 예는 W3Schools 에서 찾을 수 있습니다 ( 명명 된 유형 사용 제목 )이 예에는 전역 적으로 정의 된 요소가 하나만 있으므로 가능한 루트 요소가 하나만 있습니다.


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 :)


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.


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.


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.

ReferenceURL : https://stackoverflow.com/questions/8854144/xml-schema-root-element

반응형