web-dev-qa-db-fra.com

Schéma XML: élément racine

Le post suivant demande comment indiquer qu’un élément est l’élément racine d’un schéma XML:

Est-il possible de définir un élément racine dans un document XML à l'aide de schéma?

J'ai suivi le didacticiel w3schools sur XML Schema mais quelque chose n'est toujours pas clair. Prenons l'exemple du schéma 2 de http://www.w3schools.com/schema/schema_example.asp (reproduit ci-dessous pour plus de commodité). Comment ce code indique-t-il que <shiporder> est l'élément racine? L'exemple ne dit-il pas que tous les éléments sont valides en tant qu'éléments racines?

------------------ exemple ------------------------------- ---

<?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> 

----------------------- schéma ------------------------

<?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>

De mon point de vue, un schéma XML doit faire deux choses:

  1. définir ce qui peut se passer à l'intérieur de chaque noeud
  2. définir où chaque nœud peut être placé

Et il semble que l'exemple échoue au n ° 2. Aucune suggestion?

55
johngoche9999

Pour autant que je sache, tout élément défini globalement peut être utilisé comme élément racine et XML Schema n'a pas de notion permettant de spécifier ce qu'est l'élément racine. censé être.

Vous pouvez toutefois contourner ce problème en concevant correctement votre schéma XML, de sorte qu’il n’y ait qu’un seul élément défini globalement - alors seul cet élément est valide en tant qu’élément racine.

Vous trouverez un exemple à l'adresse suivante ( W3Schools (intitulé ) à l'aide de types nommés ). Cet exemple ne comporte qu'un seul élément défini globalement, et donc un seul élément racine possible.

51
Anony-Mousse

Tout le monde n'est pas d'accord avec cela, mais le fait que XML Schema ne puisse pas spécifier d'élément racine est voulu. La pensée est que si un <invoice> est valide quand c'est la seule chose dans un document, alors il est également valide s'il est contenu dans quelque chose d'autre. L'idée est que le contenu doit être réutilisable et qu'il ne soit pas permis d'empêcher quelqu'un d'utiliser un contenu valide dans le cadre d'une tâche plus importante.

(Le fait que ID et IDREF soient codés dans un document va plutôt à l’encontre de cette politique; mais le langage a été conçu par un comité assez vaste.)

21
Michael Kay

oui, tu as raison. le xsd devrait être:

<?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>

comme vous le voyez, maintenant il n'y a plus qu'un xs:element, et celui-là est le seul qui puisse être un élément racine valide :)

16
davogotland

L'inconvénient de nombreux éléments globaux est qu'ils pourraient tous être utilisés comme éléments de base pour les documents. L'avantage est que vous pouvez utiliser l'élément lors de la définition de nouveaux types, ce qui assurera que l'espace de nommage des éléments enfants correspond à celui du type parent.

Je pensais qu'il ne devrait y avoir qu'un seul élément global et que tous les types complexes devraient avoir un élément global.

1
xmlDave

Comment ce code indique-t-il qu'il s'agit de l'élément racine?

John, Ce schéma vient de définir tous les éléments et chacun d'entre eux peut être choisi comme élément racine. Si vous essayez de générer un exemple xml à partir d'un outil comme Altova XML Spy ou son type, vous devrez choisir un élément qui deviendra l'élément racine.

Donc, n'importe lequel de ces éléments peut être la racine.

Pour éviter toute ambiguïté, utilisez un élément défini globalement.

0
Sunil

Sur la base de l'exemple que vous avez fourni, il est possible de trouver le seul élément racine.

Vous pouvez obtenir une liste d'éléments globaux, puis une liste d'éléments imbriqués référencés dans complexType sous le noeud xs: sequence. L'élément racine est celui de la liste des éléments globaux mais pas de la liste des éléments imbriqués.

J'ai fait cela en utilisant la classe XmlSchemaSet dans .NET. Voici l'extrait de code:

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

La méthode d'extension 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;
            }
        }
    }
}

Mais il y a toujours des problèmes pour le général xsd en utilisant cette approche. Par exemple, dans DotNetConfig.xsd utilisé par Visual Studio pour le fichier de configuration, l'élément racine est défini comme suit:

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

Je n'ai pas encore trouvé de solution complète pour traiter toutes sortes de schémas. Continuera pour cela.

0
Johnny Qian