Utilisation des XML schémas avec 4D (1/2)

Christophe KEROMEN

L’utilisation des schémas de données pour décrire une structure XML s’est imposée dans deux domaines concernant directement 4D : l’import-export de données vers ou depuis une base de données et la description des extraits de données véhiculées par des messages SOAP dans le cadre des services Web.Contrairement à la DTD, 4D ne propose pas de moyen de générer un schéma de données correspondant à la structure de la base, ni de demander la validation d’une structure XML par rapport à un schéma associé. Cette note technique va dans un premier temps vous présenter les bases de la rédaction d’un schéma de données pour une structure 4D, puis vous proposera un exemple de code de mise en œuvre. ♪

- Titre : Utilisation des XML schémas avec 4D (1/2)

- Auteur : Christophe KEROMEN

- Parution : 1 août 2005

- Licence : Ce document est issu de https://www.developpez.com et reste la propriété exclusive de son auteur. La copie, modification et/ou distribution par quelque moyen que ce soit est soumise à l'obtention préalable de l'autorisation de l'auteur.

Sommaire

- I. Introduction

- II. XML schémas

- Les trois couches

- Pourquoi utiliser des schémas ?

- III. Présentation des schémas à partir d’un exemple

- Export XML d’une liste d’enregistrements

- Recours à un utilitaire

- Commentaire de la syntaxe

- Conclusion

- IV. Génération d’un schéma depuis 4D

- La table CD complétée

- La structure de la méthode de génération

- Ajout de documentation

- Reprendre la documentation existante

- Champs uniques et clés primaires

- Déclaration des types des champs

- Définition des types simples

- Restriction des types de base

- Exemple d’énumération

- Déclaration des types complexes

- Exemple de visualisation graphique

- V. Les pièges

- Les valeurs nulles

- Les booléens

- Les numériques

- Les binaires

- Association du schéma externe

- VI. Validation et tests

- Comment faire tousser oXygen

- Et sourciller XMLSpy

- VII. Conclusion

- VIII. Références

- IX. Base exemple

I - Introduction

L’utilisation des schémas de données pour décrire une structure XML s’est imposée dans deux domaines concernant directement 4D :

- l’import-export de données vers ou depuis une base de données ;

- la description des extraits de données véhiculées par des messages SOAP dans le cadre des services Web.

Contrairement à la DTD, 4D ne propose pas de moyen de générer un schéma de données correspondant à la structure de la base, ni de demander la validation d’une structure XML par rapport à un schéma associé.

Cette note technique va dans un premier temps vous présenter les bases de la rédaction d’un schéma de données pour une structure 4D, puis vous proposera un exemple de code de mise en œuvre.

Dans une seconde partie, nous verrons comment il est possible en recourant à des bibliothèques externes de demander des validations tant sur OSX que sur Windows.

II - XML schémas

Les trois couches

La démarche à l’origine de XML consiste à séparer l’information en trois couches :

- données structurées : c’est le rôle de XML ;

- description de la structure ;

- transformation et présentation finale : ces rôles sont dévolus à XSL-T et XSL-FO.

Pour décrire la structure, le modèle de contenu d’un document XML, le standard reposait initialement sur les DTD : Document Type Definition (Définition de type de document). Issue de l’ancêtre SGML, cette approche présente plusieurs inconvénients :

- les DTD ne s’expriment pas en langage XML, il faut donc apprendre un nouveau langage et utiliser des outils compatibles spécifiquement avec les DTD ;

- les DTD prévues pour décrire des textes structurés ne permettent pas de préciser les types des données contenues dans le document ;

- la définition des contraintes possibles sur un item est éloignée de celles requises par une base de données : longueur, nombre d’occurrences, plages…

- la spécification des DTD est antérieure à celle des espaces de noms.

Rapidement, la nécessité de proposer un autre standard de description s’est fait sentir. Soutenu en particulier par Microsoft, le W3C a validé en 2001 la recommandation XML Schéma, plus adaptée que les DTD aux bases de données. D’autres formalisations existent : Schematron, Relax…

Pourquoi utiliser des schémas ?

Le schéma de données est une pièce maîtresse de l’architecture XML.

Lors de l’édition d’un document dans un éditeur spécialisé, le schéma associé au document va permettre de vérifier très précisément les données saisies par l’utilisateur et la structure du document en cours.

Microsoft s’appuie sur cette fonctionnalité dans Office 2003 pour proposer les documents « intelligents » (smart documents).

Ex. : dans Word 2003 Windows

Figure 1 : édition « intelligente » dans Word s’appuyant sur un XML schéma

Figure 2 : validation par MS Word lors d’une tentative d’enregistrement

Lors d’import de données depuis une structure XML, la validation par rapport au schéma associé va permettre de s’assurer que les données fournies sont bien conformes avant de déclencher le moindre traitement de mise à jour au sein de la base de données, économisant ainsi des ressources (transactions…).

Le recours à XML lors de publication de données depuis 4D vers d’autres applications se répand de plus en plus. Même si ces applications cibles sont en général capables d’analyser directement un échantillon de structure XML, leur fournir un schéma permettra une plus grande précision, voir le paragraphe « Commentaire de la syntaxe » pour un approfondissement de cette question. C’est par exemple le cas lors d’échanges entre 4D et Macromedia Flash.

Le langage de description des services Web (WSDL) s’appuie sur les schémas XML pour décrire les structures des données échangées dans les messages SOAP. Les assistants de découverte des services Web exploitent cette description pour proposer la génération de code dans les méthodes d’appel, c’est le cas de l’assistant 4D.

Cependant, il est fréquent que le WSDL précise uniquement qu’une structure XML est attendue, sans donner plus d’information sur sa formalisation. C’est en particulier souvent le cas dans les services Web reposant sur le framework .NET, faute généralement de connaissance suffisante des fonctionnements attendus hors plateforme Microsoft. L’entité gérant le service propose parfois des compléments de description de certaines données échangées sous la forme de schémas XML. Avant envoi d’une structure XML à un service Web, il devient alors nécessaire de valider les informations par rapport au schéma fourni.

Il n’existe pas de commande dans 4D 2004.2 :

- pour générer un schéma ;

- pour valider un document par rapport à un schéma.

Le besoin existant et les fonctionnalités correspondantes manquant à l’appel, la suite de cette note se propose de vous fournir les éléments nécessaires pour utiliser les schémas dans vos applications…

III - Présentation des schémas à partir d’un exemple

Export XML d’une liste d’enregistrements

Reprenons l’exemple de la NT 4D-200410-28-FR « Générer du XML ». Un module générique nous permettait d’obtenir un export XML depuis une sélection d’enregistrements, par exemple pour la table [CD] :

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
<!-- Ex. de génération'SAX' le 08/07/2005-->
<CD>
<CD.ID_CD>5</CD.ID_CD>
<CD.Titre>4 Stabat mater</CD.Titre>
<CD.Interprète>Divers</CD.Interprète>
<CD.Genre>Musique sacrée</CD.Genre>
<CD.Description>
Ce double CD réédité par EMI en 1995 réunit 4 Stabat mater. Celui de Rossini interprété par
l’orchestre Symphonique de Berlin dirigé par Karl Forster. Il est suivi de l’œuvre de Verdi, Philharmonia Orchestra
dirigé par Carlo Maria Giulini. Sur le deuxième CD, on trouve Francis Poulenc interprété par Régine Crespin.
Cette compilation se termine avec une version moins connue, celle du polonais Karol Szymanowski.
Orchestre Symphonique de la Radio Nationale polonaise dirigée par Antoni Wit
</CD.Description>
<CD.Prix>8</CD.Prix>
<CD.DateStock>2003-01-01T00:00:00</CD.DateStock>
</CD>
...
</root>

À quoi ressemble un schéma XML décrivant cette structure ?

Recours à un utilitaire

Des outils permettent de générer un schéma à partir d’un document XML. Appliquons cette technique au résultat de notre export.

Voici le schéma généré par oXygen (sélectionner le menu 'Document/Document XML/Convertir vers…' puis choisir 'W3C XML schema', spécifier un chemin sur le disque puis cliquer sur 'Convertir'). Nous obtenons le résultat suivant :

http://www.oxygenxml.com

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" ref="CD"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="CD">
<xs:complexType>
<xs:sequence>
<xs:element ref="CD.ID_CD"/>
<xs:element ref="CD.Titre"/>
<xs:element ref="CD.Interprète"/>
<xs:element ref="CD.Genre"/>
<xs:element ref="CD.Description"/>
<xs:element ref="CD.Prix"/>
<xs:element ref="CD.DateStock"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="CD.ID_CD" type="xs:integer"/>
<xs:element name="CD.Titre" type="xs:string"/>
<xs:element name="CD.Interprète" type="xs:string"/>
<xs:element name="CD.Genre" type="xs:string"/>
<xs:element name="CD.Description" type="xs:string"/>
<xs:element name="CD.Prix" type="xs:string"/>
<xs:element name="CD.DateStock" type="xs:NMTOKEN"/>
</xs:schema>

Nous n’avions pas menti : il s’agit bien d’une structure XML ! Nous allons voir dans le paragraphe suivant que cette structure n’est pas bien compliquée.

Commentaire de la syntaxe

La déclaration de l’élément racine référence un espace de noms obligatoire, celui des schémas de données du W3C :

<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">

Tous les éléments constitutifs du schéma apparaîtront par la suite préfixés par xs.

Le schéma précise ensuite que la racine du document XML décrit aura pour nom 'root' et sera un type complexe composé de n éléments CD, n variant entre 1 et une valeur indéfinie (unbounded). L’élément <xs :sequence> spécifie que les éléments enfants doivent apparaître exactement dans l’ordre énoncé :

<xs:element name="root">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" ref="CD"/>
</xs:sequence>
</xs:complexType>
</xs:element>

Notez l’emploi de l’attribut ref qui référence un type d’élément décrit un peu plus loin dans le schéma.

Puis la structure complexe d’un élément CD (correspondant à un enregistrement de la table [CD]) est décrite, là encore en référençant des types externes par l’attribut ref :

<xs:element name="CD">
<xs:complexType>
<xs:sequence>
<xs:element ref="CD.ID_CD"/>
<xs:element ref="CD.Titre"/>
<xs:element ref="CD.Interprète"/>
<xs:element ref="CD.Genre"/>
<xs:element ref="CD.Description"/>
<xs:element ref="CD.Prix"/>
<xs:element ref="CD.DateStock"/>
</xs:sequence>
</xs:complexType>
</xs:element>

Enfin, chaque type simple est listé :

<xs:element name="CD.ID_CD" type="xs:integer"/>
<xs:element name="CD.Titre" type="xs:string"/>
<xs:element name="CD.Interprète" type="xs:string"/>
<xs:element name="CD.Genre" type="xs:string"/>
<xs:element name="CD.Description" type="xs:string"/>
<xs:element name="CD.Prix" type="xs:string"/>
<xs:element name="CD.DateStock" type="xs:NMTOKEN"/>

Nous retrouvons ici les éléments correspondant aux champs de la table [CD]. L’utilitaire a tenté de déduire les types à partir des échantillons de valeurs rencontrées dans l’extrait proposé. Ces types ne sont pas forcément précis ou adaptés.

Rappelons la structure de notre table [CD] :

Figure 3 : la table [CD]

Dans le schéma de données obtenu :

- les longueurs des champs alphanumériques ne sont pas précisées ;

- l’élément CD.PRIX est considéré comme un xs :string alors qu’il s’agit en réalité d’un réel et CD.DateStock qui est une date est typé en xs :NMTOKEN ;

- des précisions importantes, champs obligatoires, uniques, énumérés, ne sont pas mentionnées.

Conclusion

Il est possible d’obtenir un schéma en partant d’une structure XML existante. Un tel schéma décrit des types complexes par combinaison d’autres types complexes ou de types simples.

La description de tous les types simples figure dans le document maintenu par le W3C :

http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/

Cependant le schéma obtenu manque de précision. Bref, l’outil nous rend service, mais ne nous dispense pas de creuser la question !

IV - Génération d’un schéma depuis 4D

La table CD complétée

Afin de présenter un exemple le plus représentatif possible, nous avons complété, par rapport à la note technique 4D-200410-28-FR, notre table [CD] de quelques nouveaux types de données :

Structure : CD

- ID_CD Entier Long Indexée; Unique; Obligatoire; Modifiable

- Titre Alpha 80 Indexée; Obligatoire; Saisissable; Modifiable

- Interprète Alpha 80 Enumérée; Obligatoire; Saisissable; Modifiable

- Genre Alpha 20 Enumérée; Obligatoire; Saisissable; Modifiable

- Pochette Image Saisissable; Modifiable

- Description Texte Saisissable; Modifiable

- Prix Numérique Saisissable; Modifiable

- Date saisie Date Saisissable; Modifiable

- Durée_totale Heure Saisissable; Modifiable

- CD_Double Booléen Saisissable; Modifiable

La structure de la méthode de génération

Un schéma de données est un document XML que nous pouvons par conséquent générer avec les commandes dédiées de 4D 2004. Nous choisirons l’approche que 4D nomme « SAX » consistant à créer un document disque, puis à écrire les informations séquentiellement dans ce document.

Voici le pseudocode de la méthode XSD_genere qui va remplir cette tâche :

Déclaration et affectation des paramètres
Création du doc XML sur disque
Détermination du nom de l’élément correspondant à un enregistrement
Écriture des « options » du document XML
Racine + namespace
Commentaires
Déclaration de l’élément racine du document XML à décrire
Les éléments simples = les champs
un enregistrement = élement complexe composé de champs

Nous allons détailler certains de ces points ci-dessous. Pour le code complet de la méthode, merci de vous reporter à la base démo accompagnant cette note.

Ajout de documentation

Les directives XML ne font pas obligation aux applications XML de conserver les commentaires XML. Il est donc problématique de prévoir de véhiculer des précisions concernant un schéma sous cette forme. C’est pourquoi les concepteurs ont rajouté la notion d’annotation : des commentaires structurés sous forme d’éléments qui peuvent s’inclure dans tout élément existant. Un élement <xs :annotation> contient un enfant <xs :documentation> dont la valeur correspond au commentaire. La langue dans laquelle est exprimé le commentaire est décrite par l’attribut xml :lang de l’élément documentation.

Ex. :

<xs:annotation>
<xs:documentation xml:lang="fr">Schéma XSD des enregistrements de la table CD</xs:documentation>
</xs:annotation>

La petite routine XSD_set_documentation permet de rajouter ce type de commentaire :

` Methode : XSD_set_documentation

` Description :
` ajoute un commentaire ds un schéma XSD (annotation/documentation)
`
` Parametres :
` $1:HEURE:ref doc SAX contenant le schéma
` $2:TEXTE:commentaire à ajouter
` {$3:TEXTE:langue, fr par défaut}
`
` Version : 2
` Appel : XSD_set_documentation(ref doc SAX;commentaire{;langue})
` ----------------------------------------------------

C_HEURE($1;$_vh_refDoc)
$_vh_refDoc:=$1

C_TEXTE($2;$_vt_commentaire)
$_vt_commentaire:=$2

C_TEXTE($3;$_vt_langue)
Si (Nombre de parametres>2)
$_vt_langue:=$3
Sinon
$_vt_langue:="fr"
Fin de si

`<annotation>
`<documentation xml:lang="en">
`Report schema for Example.com
`Copyright 2000 Example.com. All rights reserved.
`</documentation>
` </annotation>

SAX OUVRIR ELEMENT XML($_vh_refDoc;"xs:annotation")

SAX OUVRIR ELEMENT XML($_vh_refDoc;"xs:documentation";"xml:lang";$_vt_langue)
SAX AJOUTER VALEUR ELEMENT XML($_vh_refDoc;$_vt_commentaire)
SAX FERMER ELEMENT XML($_vh_refDoc)

SAX FERMER ELEMENT XML($_vh_refDoc)   `"xs:annotation"

Reprendre la documentation existante

Avec le renfort du plugin DynamicStructure d’Osmose éditeur, il est ainsi possible de reprendre dans le schéma des commentaires de tables et de champs rédigés dans la zone commentaire de l’explorateur 4D ou bien les informations saisies dans la zone Infobulles :

Figure 4 : la zone Infobulles

Exemple de méthode exploitant DynamicStructure pour écrire de la documentation dans le schéma :

` Methode : XSD_set_FieldTip

` Description :
` ex. de récupération d’une infobulle par DynamicStructure
`
` Parametres :
` $0:TEXTE:Ref Element cree
`
` Version : 1
` Appel : XSD_set_FieldTip ($_vh_refDoc;$_ve_NoTable;$j)
` ----------------------------------------------------

C_HEURE($1;$_vh_refDoc)
$_vh_refDoc:=$1

C_ENTIER($2;$_ve_NoTable)
$_ve_NoTable:=$2

C_ENTIER($3;$_ve_NoChamp)
$_ve_NoChamp:=$3

C_ENTIER($err)
C_TEXTE($_vt_fieldTip)
$err:=ds_GetFieldTip ($_ve_NoTable;$_ve_NoChamp;$_vt_fieldTip)

Si ($_vt_fieldTip#"")
XSD_set_documentation ($_vh_refDoc;$_vt_fieldTip)
Fin de si

Champs uniques et clés primaires

Dans notre table [CD], le champ ID_CD est précisé comme devant être unique, 4D ne permettant pas de spécifier qu’un champ est une clé primaire. La description correspondante dans un schéma XML est un peu complexe et fait appel à des chemins XPath pour préciser l’élément unique dans le document.

Voici le résultat obtenu :

<xs:unique name="ID_CD">
<xs:selector xpath=".//CD"/>
<xs:field xpath="CD.ID_CD"/>
</xs:unique>

L’élément <xs :unique> spécifie dans son attribut name le nom attribué à l’entité unique, ici « ID_CD ». Cette entité virtuelle est décrite par deux expressions XPath permettant de déterminer l’unicité de la valeur dans la structure.

L’élément enfant <xs:selector> précise dans son attribut xpath l’ensemble des nœuds à considérer. L’autre élément enfant <xs :field> précise dans son attribut xpath le critère à évaluer pour déterminer l’unicité de la valeur. Ici « CD.ID_CD » suffit, puisque nous avons arbitrairement choisi de préfixer les noms de champs 4D par le nom de leur table d’appartenance. Un cas fréquent fait intervenir la valeur d’un attribut précis de l’élément considéré.

La méthode XSD_genere permettant d’obtenir ce code pour un champ 4D dont nous avons détecté l’attribut unique grâce à un appel à LIRE PROPRIETES CHAMP :

` Methode : XSD_setElementUnique

` Description :
` définition d’élément unique relativement à un chemin XPath
`
` Parametres :
` $1:HEURE:RefDoc
` $2:POINTEUR:Champ 4D
` $3:TEXTE:Nom element contenant
`
` Version : 1
` Appel : XSD_set_ElementUnique(RefDoc;->Champ;nom de l’élement contenant)
` ----------------------------------------------------

C_HEURE($1;$_vh_refDoc)
$_vh_refDoc:=$1

C_POINTEUR($2;$_vp_field)
$_vp_field:=$2

C_TEXTE($3;$_vt_NomTable_XML)
$_vt_NomTable_XML:=$3

C_TEXTE($_vt_NomChamps)
$_vt_NomChamps:=XSD_getElementNameForField ($_vp_field)

SAX OUVRIR ELEMENT XML($_vh_refDoc;"xs:unique";"name";$_vt_NomChamps)

SAX OUVRIR ELEMENT XML($_vh_refDoc;"xs:selector";"xpath";".//"+$_vt_NomTable_XML)
SAX FERMER ELEMENT XML($_vh_refDoc)

C_TEXTE($_vt_NomChamps)
$_vt_NomChamps:=$_vt_NomTable_XML+"."+$_vt_NomChamps
SAX OUVRIR ELEMENT XML($_vh_refDoc;"xs:field";"xpath";$_vt_NomChamps)
SAX FERMER ELEMENT XML($_vh_refDoc)

SAX FERMER ELEMENT XML($_vh_refDoc)

Déclaration des types des champs

Pour chaque champ de la table, nous appelons la méthode XSD_set_Element_type qui sera chargée de définir le type XSD correspondant au type natif 4D. Elle va travailler en deux temps, définissant d’abord des types simples avant de rajouter des précisions par ce que l’on nomme une restriction de type.

Définition des types simples

`--- Détermination des types de base

`les RFC du W3C :
`http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/

`Traductions en français des docs du W3C :
`http://xmlfr.org/w3c/TR/xmlschema-0/
`http://xmlfr.org/w3c/TR/xmlschema-1/

C_ENTIER($_ve_TypeObjet;$_ve_Long)
C_BOOLEEN($_vb_index;$_vb_SiUnik)
LIRE PROPRIETES CHAMP($_Pt_Objet;$_ve_TypeObjet;$_ve_Long;$_vb_index;$_vb_SiUnik)

C_TEXTE($_vt_nomEnum)
C_BOOLEEN($_vb_obligatoire;$_vb_nonSaisissable;$_vb_nonModifiable)
LIRE PROPRIETES SAISIE CHAMP($_Pt_Objet;$_vt_nomEnum;$_vb_obligatoire;$_vb_nonSaisissable;$_vb_nonModifiable)

C_BOOLEEN($_vb_nillable)
$_vb_nillable:=Faux   `peut-être null au sens XML schema

Au cas ou
: ($_ve_TypeObjet=Est un champ alpha )
$_vt_Type:="xs:normalizedString"   `une string ne contenant pas de CR-Tab

`si $_ve_Long#0 fixer un maxLength
`<simpleType name='form-input'>
`<restriction base='string'>
`<maxLength value='50'/>
`</restriction>
`</simpleType>

: ($_ve_TypeObjet=Est un texte )
$_vt_Type:="xs:string"

: ($_ve_TypeObjet=Est un numérique )
$_vt_Type:="xs:float"

: ($_ve_TypeObjet=Est un entier )
$_vt_Type:="xs:short"

: ($_ve_TypeObjet=Est un entier long )
$_vt_Type:="xs:int"

`3 types possibles pour les dates et heures
`*xs:time(HH:MM:SS.sss)
`*xs:date(YYYY-MM-DD)
`*xs:dateTime(dateTtime)

: ($_ve_TypeObjet=Est une date )
$_vt_Type:="xs:Date"
$_vb_nillable:=Vrai

: ($_ve_TypeObjet=Est une heure )
$_vt_Type:="xs:time"

: ($_ve_TypeObjet=Est un booléen )
$_vt_Type:="xs:boolean"

: ($_ve_TypeObjet=Est un BLOB ) | ($_ve_TypeObjet=Est une image )   `conversion base64
$_vt_Type:="xs:base64Binary"
$_vb_nillable:=Vrai
Sinon
`erreur à traiter
TRACE
Fin de cas

Restriction des types de base

Nous traitons le cas des énumérations et des longueurs maximales de chaînes de caractères. D’autres restrictions seraient possibles pour définir des types personnalisés aussi utiles qu’une clé RIB, un numéro de sécurité sociale, une adresse email…Nous utiliserions alors les expressions régulières pour décrire des patterns.

La longueur maximale d’une chaîne de caractères s’exprime en définissant ce que l’on nomme une « facette », par exemple pour le champ Interprète :

<xs:element name="CD.Interprète" nillable="false">
<xs:simpleType>
<xs:restriction base="xs:normalizedString">
<xs:maxLength value="80"/>
</xs:restriction>
</xs:simpleType>
</xs:element>

La facette <xs:maxLength value=« 80 »/> restreint le type de base xs : normalizedString (un type string ne contenant ni tabulation, ni retour chariot) vers un nouveau type n’acceptant que des chaînes limitées à 80 caractères. Dans notre exemple, nous effectuons cette opération pour chaque champ alpha. Il serait également possible de définir un nouveau type, par exemple string80, applicable à plusieurs éléments.

Le code correspondant :

`on définit un nouveau type

SAX OUVRIR ELEMENT XML($_vh_refDoc;"xs:simpleType")
SAX OUVRIR ELEMENT XML($_vh_refDoc;"xs:restriction";"base";"xs:normalizedString")

`--- Exemple de définition d’une facette : maxLength
Si ($_ve_Long#0)
SAX OUVRIR ELEMENT XML($_vh_refDoc;"xs:maxLength";"value";Chaine($_ve_Long))   `<maxLength value='XX'/>
SAX FERMER ELEMENT XML($_vh_refDoc)
Fin de si

SAX FERMER ELEMENT XML($_vh_refDoc)   `xs:restriction
SAX FERMER ELEMENT XML($_vh_refDoc)   `xs:simpleType
SAX FERMER ELEMENT XML($_vh_refDoc)   `xs:element

Exemple d’énumération

Une énumération se traduit dans le schéma par une séquence d’éléments listant les différentes valeurs possibles, par exemple pour le champ Genre et l’énumération associée « Genres » :

<xs:element name="CD.Genre" nillable="false">
<xs:simpleType>
<xs:restriction base="xs:normalizedString">
<xs:maxLength value="20"/>
<xs:enumeration value="Chanson française"/>
<xs:enumeration value="Jazz"/>
<xs:enumeration value="Musique sacrée"/>
<xs:enumeration value="World Music"/>
</xs:restriction>
</xs:simpleType>
</xs:element>

Voici l’extrait de code correspondant :

$_vb_SimpleType:=Faux

ENUMERATION VERS TABLEAU($_vt_nomEnum;$_Tt_Enum)

SAX OUVRIR ELEMENT XML TABLEAUX($_vh_refDoc;"xs:element";$_Tt_AttName;$_Tt_AttValue)

`on définit un nouveau type par restriction du type normalizedString
SAX OUVRIR ELEMENT XML($_vh_refDoc;"xs:simpleType")
SAX OUVRIR ELEMENT XML($_vh_refDoc;"xs:restriction";"base";"xs:normalizedString")

`--- Exemple de définition d’une facette : maxLength = longueur maxi de la chaîne de caractères
Si ($_ve_Long#0)
SAX OUVRIR ELEMENT XML($_vh_refDoc;"xs:maxLength";"value";Chaine($_ve_Long))
`<maxLength value='XX'/>
SAX FERMER ELEMENT XML($_vh_refDoc)
Fin de si

C_ENTIER($i)
Boucle ($i;1;Taille tableau($_Tt_Enum))
SAX OUVRIR ELEMENT XML($_vh_refDoc;"xs:enumeration";"value";$_Tt_Enum{$i})
SAX FERMER ELEMENT XML($_vh_refDoc)
Fin de boucle

SAX FERMER ELEMENT XML($_vh_refDoc)   `xs:restriction
SAX FERMER ELEMENT XML($_vh_refDoc)   `xs:simpleType

SAX FERMER ELEMENT XML($_vh_refDoc)   `xs:element

Déclaration des types complexes

Tous les types des éléments correspondant aux champs sont maintenant définis :

<xs:element name="CD.Titre" nillable="false">
<xs:simpleType>
<xs:restriction base="xs:normalizedString">
<xs:maxLength value="80"/>
</xs:restriction>
</xs:simpleType>
</xs:element>

<xs:element name="CD.Interprète" nillable="false">
<xs:simpleType>
<xs:restriction base="xs:normalizedString">
<xs:maxLength value="80"/>
</xs:restriction>
</xs:simpleType>
</xs:element>

<xs:element name="CD.Genre" nillable="false">
<xs:simpleType>
<xs:restriction base="xs:normalizedString">
<xs:maxLength value="20"/>
<xs:enumeration value="Chanson française"/>
<xs:enumeration value="Jazz"/>
<xs:enumeration value="Musique sacrée"/>
<xs:enumeration value="World Music"/>
</xs:restriction>
</xs:simpleType>
</xs:element>

<xs:element name="CD.Pochette" nillable="true" type="xs:base64Binary"/>
<xs:element name="CD.Description" nillable="false" type="xs:string"/>
<xs:element name="CD.Prix" nillable="false" type="xs:float"/>
<xs:element name="CD.Date_saisie" nillable="true" type="xs:date"/>
<xs:element name="CD.Durée_totale" nillable="false" type="xs:time"/>
<xs:element name="CD.CD_Double" nillable="false" type="xs:boolean"/>

Il est alors facile de définir les types complexes par simple combinaison de ces types. Cela se traduit par l’extrait XML suivant :

<xs:element name="CD">
<xs:complexType>
<xs:sequence>
<xs:element ref="CD.ID_CD">
<xs:annotation>
<xs:documentation xml:lang="fr">clé primaire du CD</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element ref="CD.Titre">
<xs:annotation>
<xs:documentation xml:lang="fr">Le titre du CD</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element ref="CD.Interprète">
<xs:annotation>
<xs:documentation xml:lang="fr">L’interprète du CD</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element ref="CD.Genre">
<xs:annotation>
<xs:documentation xml:lang="fr">Le genre musical du CD</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element ref="CD.Pochette" minOccurs="0">
<xs:annotation>
<xs:documentation xml:lang="fr">la pochette au format JPEG</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element ref="CD.Description" minOccurs="0">
<xs:annotation>
<xs:documentation xml:lang="fr">Texte libre de description du CD</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element ref="CD.Prix" minOccurs="0">
<xs:annotation>
<xs:documentation xml:lang="fr">Prix de revente d’occasion</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element ref="CD.Date_saisie" minOccurs="0">
<xs:annotation>
<xs:documentation xml:lang="fr">date de saisie dans la base</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element ref="CD.Durée_totale" minOccurs="0">
<xs:annotation>
<xs:documentation xml:lang="fr">Durée totale des pistes du CD</xs:documentation>
</xs:annotation>
</xs:element>
<xs:element ref="CD.CD_Double" minOccurs="0">
<xs:annotation>
<xs:documentation xml:lang="fr">CD Double ?</xs:documentation>
</xs:annotation>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>

Gestion de l’attribut obligatoire

Notez que pour certains éléments nous ne précisons pas l’attribut minOccurs= “0“, en effet si cet attribut n’apparaît pas, sa valeur par défaut vaut 1. Cela correspond aux champs possédant l’attribut obligatoire dont l’élément correspondant devra apparaître obligatoirement dans une structure <CD>.

Voici le code 4D de définition où la routine XSD_isMandatory appelle la commande LIRE PROPRIETES SAISIE CHAMP pour savoir si le champ est obligatoire ou non :

Si (XSD_isMandatory (Champ($_ve_NoTable;$j)))  `on ne précise pas l’attribut minOccurs=0 => obligatoire
SAX OUVRIR ELEMENT XML($_vh_refDoc;"xs:element";"ref";$_Tt_NomChamps{$j})
Sinon
SAX OUVRIR ELEMENT XML($_vh_refDoc;"xs:element";"ref";$_Tt_NomChamps{$j};"minOccurs";"0")
Fin de si

Exemple de visualisation graphique

Une fois notre schéma de données généré, nous pouvons l’ouvrir dans un utilitaire pour vérifier sa validité, car il s’agit, rappelons-le encore une fois, d’un document XML. Les utilitaires spécialisés proposent généralement une vue graphique d’un schéma :

Figure 5 - vue graphique du schéma dans oXygen

Figure 6 - vue graphique du schéma dans XMLSpy

Notez en particulier, en bas à droite, la reconnaissance de notre facette Énumération pour le type CD.Genre.

V - Les pièges

Lors de la génération par 4D du document XML comprenant les enregistrements de la table [CD] nous rencontrons quelques cas particuliers qui vont nous amener à enrichir le code d’export exposé dans la note technique antérieure « Générer du XML ».

Les valeurs nulles

Ce problème est classique et ne concerne pas qu’XML. 4D ne connaît pas le type null qui indique qu’aucune valeur n’a été obtenue ou renseignée. Cela empêche de faire la différence dans certains cas entre une valeur non renseignée et une valeur précise.

Ainsi, minuit s’exprime 00 :00 :00 en xs :time, tout comme une heure non saisie dans 4D !

De même, un booléen dans 4D aura toujours une valeur, par défaut égale à faux.

En revanche, les types XSD correspondant aux BLOBs, images et Dates n’acceptent pas les valeurs à 0, il faut dans ce cas :

- déclarer que l’élément correspondant peut être null, cela s’effectue dans le schéma, par ex. pour une date <xs:element name=« CD.Date_saisie » nillable=« true » type=« xs:date »/> ;

- déclarer l’élément comme null dans la structure XML, par ex. toujours pour une date : <CD.Date_saisie xsi:nil=« true »/>.

Les booléens

En dehors de la question de la valeur nulle qui peut amener à préférer en structure l’utilisation d’un type entier au type booléen, la conversion consiste simplement à remplacer la valeur 4D par la chaîne true ou false.

` Methode : XML_get_XSD_boolean

` Description :
` retourne une valeur convertie en xs:boolean depuis un booléen 4D
`
` Parametres :
` $1:BOOLEEN:valeur à convertir
` $0:TEXTE:valeur convertie en xs:boolean
`
` Version : 1
` Appel : valeur convertie:=XML_get_XSD_boolean(booléen)
` ----------------------------------------------------

C_BOOLEEN($1)
C_TEXTE($0)

`The canonical representation for boolean is the set of literals {true, false}.

$0:=("true"*Num($1))+("false"*Num(Non($1)))

Les numériques

4D ne reconnaît qu’un seul type de numérique : le réel flottant.

Dans la pratique, malgré son imprécision liée à la virgule flottante, ce type est souvent utilisé pour stocker des nombres avec un nombre fixe de décimales, des montants par exemple.

Lors de la conception d’un schéma, le développeur a donc le choix entre associer le numérique 4D à un xs :float ou un xs :decimal.

Autre point à considérer, le séparateur décimal est officiellement le point. Il faut donc remplacer la virgule des numériques 4D par un point avant l’export.

La méthode suivante traite le cas du Type xs :decimal :

` Methode : XML_get_XSD_decimal
` Description :
` convertit un numérique 4D en xs:decimal
` par défaut on considère qu’il s’agit d’un decimal et non d’un float
`
` Parametres :
` $1: NUMERIQUE:valeur du champ
` $2:ENTIER:nbre de décimales à conserver
` $0:TEXTE:valeur convertie
`
` Version : 1
` Appel : valeur convertie:=XML_get_XSD_decimal(valeur du champ)
` ----------------------------------------------------

C_REEL($1;$_vr_value)
$_vr_value:=$1

C_ENTIER($2;$_ve_decimales)
$_ve_decimales:=$2

C_TEXTE($0;$_vt_Value)
$0:=""

$_vr_value:=Arrondi($_vr_value;$_ve_decimales)

$_vt_Value:=Chaine($_vr_value)

`compatibilité XML schema : le séparateur est le .
$_vt_Value:=Remplacer chaine($_vt_Value;",";".")

$0:=$_vt_Value

Les binaires

La version précédente du code d’export XML ne gérait pas les données binaires. La recommandation du W3C propose le type xs :binary codé en base64 pour véhiculer ce type de données. La méthode ci-dessous respecte ce type en mettant à profit la nouvelle commande ENCODER de 4D 2004.

Attention, cependant le traitement des images s’effectue en les transformant automatiquement en JPEG ce qui, bien entendu, n’est pas toujours la bonne solution.

` Methode : SAX_ajouter_XSD_binary

` Description :
` convertit une valeur BLOB ou Image en xs:binary et l’ajoute sous forme de valeur d’élement
` au doc SAX courant
`!! les images sont automatiquement converties en JPEG !!
`
` Parametres :
` $1:POINTEUR:valeur à convertir
`
` Version : 1
` Appel : SAX_ajouter_XSD_binary(RefDoc;-> valeur;nom element)
` ----------------------------------------------------
`[Definition:] base64Binary represents Base64-encoded arbitrary binary data.
`The ·value space· of base64Binary is the set of finite-length sequences of binary octets.
`For base64Binary data the entire binary stream is encoded using the Base64
`  Content-Transfer-Encoding defined in Section 6.8 of [RFC 2045].

C_HEURE($1;$_vh_refDoc)
$_vh_refDoc:=$1

C_POINTEUR($2;$_vp_value)
$_vp_value:=$2

C_TEXTE($3;$_vt_TagName)
$_vt_TagName:=$3

C_ENTIER LONG($_vl_TypeChamp)
$_vl_TypeChamp:=Type($_vp_value->)

Au cas ou
: ($_vl_TypeChamp=Est un BLOB )   `conversion base64
Si (Taille BLOB($_vp_value->)>0)
$_vx_BLOB:=$_vp_value->
ENCODER($_vx_BLOB)
SAX_AJOUTER_ELEMENT_SIMPLE ($_vh_refDoc;$_vt_TagName;"";->$_vx_BLOB)
Sinon
SAX OUVRIR ELEMENT XML($_vh_refDoc;$_vt_TagName;"xsi:Nil";"true")
SAX FERMER ELEMENT XML($_vh_refDoc)
Fin de si

: ($_vl_TypeChamp=Est une image )   `conversion JPEG puis base64
Si (Taille image($_vp_value->)>0)
IMAGE VERS BLOB($_vp_value->;$_vx_BLOB;"JPEG")
ENCODER($_vx_BLOB)
SAX_AJOUTER_ELEMENT_SIMPLE ($_vh_refDoc;$_vt_TagName;"";->$_vx_BLOB)
Sinon
SAX OUVRIR ELEMENT XML($_vh_refDoc;$_vt_TagName;"xsi:Nil";"true")
SAX FERMER ELEMENT XML($_vh_refDoc)
Fin de si

Fin de cas

Du poids des binaires dans le texte

Il faut noter que les documents XML peuvent alors devenir très lourds. L’encodage en base64 rajoute environ 33% de poids aux champs binaires qui peuvent déjà prendre pas mal de place. Le traitement de tels types de documents XML va nécessiter beaucoup de ressources processeur et mémoire. Ainsi le document généré par la base de démonstration et comprenant six enregistrements va peser 1,3 Mo sur disque. Voici les temps de chargement et d’analyse dans un parseur validant sur un G4 1GHz :

Loading XML document '/Volumes/Proxima/Users/ckeromen/Altair/CKTI_Informatique/4D_NT_CK/XML
schemas/4DXMLschema/CD_root.XML' (1343ms)

Loading schema 'file:/Volumes/Proxima/Users/ckeromen/Altair/CKTI_Informatique/4D_NT_CK/XML%20schemas/4DXMLschema/
CD_root.XSD' (3600ms)

Validating '/Volumes/Proxima/Users/ckeromen/Altair/CKTI_Informatique/4D_NT_CK/XML
schemas/4DXMLschema/CD_root.XML' (1674ms)

Ces temps sont loin d’être négligeables surtout si on les compare aux temps nécessaires aux mêmes processus sur le même document SANS les binaires encodés en base64 !

Loading XML document '/Volumes/Proxima/Users/ckeromen/Altair/CKTI_Informatique/4D_NT_CK/XML
schemas/4DXMLschema/CD_root.XML' (728ms)

Loading schema 'file:/Volumes/Proxima/Users/ckeromen/Altair/CKTI_Informatique/4D_NT_CK/XML%20schemas/4DXMLschema/
CD_root.XSD' (3812ms)

Validating '/Volumes/Proxima/Users/ckeromen/Altair/CKTI_Informatique/4D_NT_CK/XML
schemas/4DXMLschema/CD_root.XML' (118ms)

Vous pouvez constater l’intérêt de conserver les binaires HORS de la structure XML…Ce point mériterait d’ailleurs à lui seul une nouvelle note, mais vous pouvez consulter la note technique 4D-200112-38-FR « Archivage de données au format XML » par Thomas MAUL, qui propose un exemple de gestion propriétaire des binaires en externe.

À noter également le temps de validation qui reste stable, en effet, il dépend de la complexité du schéma et du nombre d’éléments du document.

Association du schéma externe

Quoiqu’un schéma puisse, tout comme une DTD, être interne à un document XML, c’est assez rarement le cas. L’association d’un document avec un schéma externe s’écrit de la manière suivante (ici l’URL du schéma est indiquée de manière relative au document XML lui-même) :

<CD_root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="CD_root.XSD">

Ce qui donne dans 4D :

SAX OUVRIR ELEMENT XML ($_vh_refDoc;$_vt_root;"xmlns:xsi";"http://www.w3.org/2001/XMLSchema-instance";"xsi:noNamespaceSchemaLocation";$_vt_modelePath)

VI - Validation et tests

Nous sommes maintenant capables de demander aux utilitaires externes de valider notre export par rapport au schéma associé. Pour vérifier les règles décrites dans le schéma, nous pouvons nous amuser à modifier manuellement certaines valeurs de l’export et constater que le document modifié devient alors invalide…

Comment faire tousser oXygen

Dans ce premier exemple, nous avons modifié le nom d’un élément (<CD.Interprète2>) qui n’appartient donc pas au schéma, oXygen nous l’indique, surpris :

Figure 7 - introduction d’un élément invalide

Et sourciller XMLSpy

Dans le second exemple, nous avons modifié la valeur de <CD.Genre> en saisissant une chaîne ne faisant pas partie de l’énumération décrite dans le schéma. Cette fois, c’est à XMLSpy que nous avons demandé son avis…

Figure 8 - ici le document est invalide, car « Blues » n’appartient pas à l’énumération

VII - Conclusion

Nous avons présenté dans cette note l’intérêt de l’utilisation des schémas de données. Après avoir brièvement introduit la syntaxe de rédaction des schémas, nous avons commenté un exemple de code générique générant un schéma de données pour une table 4D. Puis, nous avons vu dans des utilitaires spécialisés comment la définition de ce schéma permettait de contrôler précisément le contenu de l’export XML.

Mission accomplie pour cette première étape !

Dans la suite de cette note, nous nous attacherons à déclencher la validation du schéma depuis 4D…

VIII - Références

http://www.w3.org/TR/2001/REC-xmlschema-2-20010502/

Traductions en français des docs du W3C :

http://xmlfr.org/w3c/TR/xmlschema-0/

http://xmlfr.org/w3c/TR/xmlschema-1/

Glossaire XML en français

http://www.educnet.education.fr/dossier/xml/glossaire.htm

Le livre en français d’Eric Van Vlist : « XML schéma » chez O’Reilly, ISBN : 2-84177-215-2

« XML Application Development with MSXML 4.0 »

* Editeur(s) : Wrox Press
* Auteur(s) : S.Mohr S.Livingstone-Perez D.Singh D.Ayers M.Corning
* Parution : 03/04/2002
* Edition : 1re edition
* Nb de pages : 774 pages

En anglais

IX - Base exemple

Téléchargez la base exemple :

base exemple