Nel precedente articolo JAXB: l’XML in Java si è introdotto il framework JAXB (Java Architecture for XML Binding), un framework a supporto dell’utilizzo dell’XML in Java, descrivendone i principali strumenti: bind, marshal e unmarshal. In questo articolo si approfondirà la conoscenza del binding mostrando come possa essere customizzato per venire incontro a particolari esigenze, estendendo le potenzialità di riuso degli schemi XML a disposizione.
Generalmente le classi generate dal binding compiler sono sufficienti a soddisfare le esigenze degli utenti. In alcuni casi però può risultare utile o necessario modificare il comportamento di default. I principali motivi per customizzare sono:
- Creare la documentazione per package, classi, metodi e costanti generati a partire dagli schemi XML.
- Modificare i nomi per risolvere situazioni che il mapping automatico XML à Java non è in grado di gestire (ad esempio per risolvere le collisioni).
- Sovrascrivere il binding di default per ottenere comportamenti specifici (ad esempio è possibile specificare che un attributo fisso può essere associato a una costante Java, oppure si possono unificare i package).
Scopo di questo articolo è descrivere i meccanismi alla base della customizzazione e fornire diversi esempi pratici per applicare i principi descritti. Verrà infine mostrato come si manifestano le collisioni e come porvi rimedio. L’articolo è costituito dalle seguenti sezioni:
- Precondizioni
- L’organizzazione a livelli e sintassi
- Modalità e primi esempi
- Risoluzione di collisioni, un esempio approfondito
Precondizioni
Assicurarsi di avere correttamente installato sul computer il Java Development Kit (JDK 6, Update 24 o successiva). Si assume che sia stata correttamente impostata la variabile d’ambiente JAVA_HOME
e aggiornato il Path
di sistema.
É inoltre utile avere conoscenze basiche sull’argomento, quindi occorre aver affrontato gli argomenti XML e XML schema, consigliamo la lettura di “XML Schema”. Infine, è utile avere conoscenze riguardanti il framework JAXB, per approfondire fare riferimento all’articolo precedentemente indicato.
L’organizzazione a livelli e sintassi
La customizzazione avviene tramite dichiarazioni, dette anche binding declarations. L'XML binding può essere customizzato su quattro livelli differenti (anche chiamati scopes), livelli che racchiudono l’insieme degli elementi sui quali si applicano le dichiarazioni realizzate. Si riporta di seguito l’organizzazione degli scopes:
- Component Scope
- Definition Scope
- Schema Scope
- Global Scope
É possibile vedere questi livelli come una piramide in cui le dichiarazioni a livello più alto (il livello più alto è il Component Scope) sovrascrivono ed ereditano da quelle a livello più basso. Ad esempio, valori definiti nello schema scope sovrascrivono valori ereditati dal global scope. Segue la descrizione di base dei livelli di customizzazione con la sintassi delle relative dichiarazioni.
Global Scope
Questo livello è usato per determinare il comportamento del binding compiler rispetto a specifiche circostanze, ad esempio è possibile stabilire il comportamento del mapping nel caso di presenza di un carattere underscore. Tale livello copre tutti gli elementi nello schema padre e ricorsivamente gli altri schemi XML che sono importati o inclusi. Segue la sintassi:
<globalBindings>
[ collectionType = "collectionType" ]
[ fixedAttributeAsConstantProperty = "true" | "false" | "1" | "0" ]
[ generateIsSetMethod = "true" | "false" | "1" | "0" ]
[ enableFailFastCheck = "true" | "false" | "1" | "0" ]
[ choiceContentProperty = "true" | "false" | "1" | "0" ]
[ underscoreBinding = "asWordSeparator" | "asCharInWord" ]
[ typesafeEnumBase = "typesafeEnumBase" ]
[ typesafeEnumMemberName = "generateName" | "generateError" ]
[ enableJavaNamingConventions = "true" | "false" | "1" | "0" ]
[ bindingStyle = "elementBinding" | "modelGroupBinding" ]
[ <javaType> ... </javaType> ]*
</globalBindings>
E’ possibile definire il comportamento di livello Global solo una volta, in corrispondenza dello schema XML padre.
Schema Scope
Livello utilizzato per stabilire regole di mapping riguardanti nomi di package e altre regole specifiche per i package. E’ ad esempio possibile stabilire suffissi o prefissi, o unire diversi package in uno. Il livello copre tutti gli elementi target dello schema indicato. Segue la sintassi:
<schemaBindings>
[ <package> package </package> ]
[ <nameXmlTransform> ... </nameXmlTransform> ]*
</schemaBindings>
<package [ name = "packageName" ]
[ <javadoc> ... </javadoc> ]
</package>
<nameXmlTransform>
[ <typeName [ suffix="suffix" ]
[ prefix="prefix"] /> ]
[ <elementName [ suffix="suffix" ]
[ prefix="prefix"] /> ]
[ <modelGroupName [ suffix="suffix" ]
[ prefix="prefix" ] /> ]
[ <anonymousTypeName [ suffix="suffix" ]
[ prefix="prefix" ] /> ]
</nameXmlTransform>
Definition e Component Scope
I due livelli precedenti sono caratterizzati da uno specifico tag, mentre ciò non avviene per i livelli più alti della piramide. Il definition scope copre tutti gli elementi che fanno riferimento a definizioni di tipo o a dichiarazioni globali, mentre il component scope copre unicamente gli elementi di uno specifico schema XML.
Queste dichiarazioni hanno lo scopo di customizzare nome o implementazione di classi derivate dagli schemi XML. Segue la sintassi:
Class Binding Declarations
Dopo aver descritto i livelli, segue la descrizione e la sintassi di dichiarazioni che non si collocano su un livello specifico.
<class [ name = "className"]
[ impl"implClass" ] >
[ <javadoc> ... </javadoc> ]
</class>
Property Binding Declarations
Dichiarazioni di questo tipo hanno lo scopo di customizzare il binding di elementi degli schemi XML nelle rispettive proprietà in Java. Lo scope di questa customizzazione può essere tanto di livello definition quanto di livello component, a seconda di dove viene specificata la dichiarazione <property>. Segue la sintassi:
<property [ name = "propertyName"]
[ collectionType = "propertyCollectionType" ]
[ fixedAttributeAsConstantProperty = "true" | "false" | "1" | "0" ]
[ generateIsSetMethod = "true" | "false" | "1" | "0" ]
[ enableFailFastCheck ="true" | "false" | "1" | "0" ]
[ <baseType> ... </baseType> ]
[ <javadoc> ... </javadoc> ]
</property>
javaType Binding Declarations
L’XML fornisce più data types di quelli del linguaggio Java. La dichiarazione <javaType> consente di specificare come effettuare il mapping quando il comportamento di default non rispecchia le intenzioni. Il data type sul quale si vuole effettuare il mapping può essere tradotto verso un tipo nativo o verso un tipo specifico di un’applicazione. Nel caso di tipi specifici, l’implementazione dovrà fornire metodi di parse
e di print
per l’unmarshalling
e il marshalling
dei dati. Per questo fine vengono forniti i metodi parseMethod
e printMethod
. La dichiarazione <javaType> può essere usata in dichiarazioni di livello global
o property
, o in annotazioni per definizioni di tipi semplici. Segue la sintassi:
<javaType name="javaType" >
[ xmlType = "xmlType" ]
[ hasNsContext = "true" | "false" ]
[ parseMethod = "parseMethod" ]
[ printMethod = "printMethod" ]>
Typesafe Enumeration Bindong Declarations
É possibile effettuare il mapping elementi XML simpleType a classi Java typesafe enum, attraverso due tipi di dichiarazioni:
- <typesafeEnumClass> per il mapping di un’intera classe simpleType verso classi typesafe enum
- <typesafeEnumMember> se il mapping deve riguardare solo i membri selezionati di una classe simpleType verso classi typesafe enum
Vi sono da tenere a mente due restrizioni. La prima è che è possibile adoperare questa customizzazione solo su elementi simpleType che prevedano enumeration facets. La seconda prevede che si possa customizzare una sola definizione alla volta, altrimenti occorre utilizzare uno degli attributi della dichiarazione di livello global. Segue la sintassi:
<typesafeEnumClass
[ name = "enumClassName" ]
[ <typesafeEnumMember> ... </typesafeEnumMember> ]
[ <javadoc> enumClassJavadoc </javadoc> ]
</typesafeEnumClass>
<typesafeEnumMember name = "enumMemberName">
[ value = "enumMemberValue" ]
[ <javadoc> enumClassJavadoc </javadoc> ]
</typesafeEnumMember>
Javadoc Binding Declarations
La dichiarazione <javadoc> consente di aggiungere annotazioni Javadoc a package, classi, interfacce, metodi e attributi generati. Da osservare che non è applicabile a livello globale in quanto valida solo come sotto elemento di altre customizzazioni. Segue la sintassi:
<javadoc>
Contents in Javadoc format.
</javadoc>
Modalità e primi esempi
Abbiamo visto che la customizzazione avviene sotto forma di dichiarazioni passate al binding compiler. Queste dichiarazioni possono essere realizzate in due forme:
- Annotazioni nello schema XML (inline annotations)
- Dichiarazioni in un file esterno (external customizations file)
La prima modalità potrebbe risultare più semplice da adottare in quanto, essendo effettuata direttamente nel contesto di interesse, risulta più immediata e non richiede di descrivere i path, ossia i percorsi necessari a individuare la posizione nello schema XML in cui effettuare le modifiche. Di contro un file esterno presenta altri vantaggi, permettendo ad esempio di customizzare più schemi in contemporanea senza modificare gli schemi sorgenti. Questi concetti verranno ripresi nel seguito del paragrafo.
Gli elementi per customizzare il binding sono definiti nel namespace http://java.sun.com/xml/ns/jaxb, per cui occorrerà aggiungere questa dichiarazione nel file di binding esterno o nello schema in cui si volesse utilizzare una annotazione inline.
Customization Namespace Prefix
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:jxb=”http://java.sun.com/xml/ns/jaxb” jxb:version=”2.0” >
Come riportato, è necessario specificare la versione utilizzata, attualmente la 2.0.
Customizzazione inline
La customizzazione inline avviene aggiungendo le dichiarazioni direttamente nello schema XML, dichiarazioni che vanno innestate negli elementi <xs:annotation> e <xs:appinfo>, come mostrato di seguito:
<xs:annotation>
<xs:appinfo>
... binding declarations ...
</xs:appinfo>
</xs:annotation>
Nell’esempio che si propone, vedremo come modificare il nome del package derivato dal mapping di uno schema XML. Partiamo dallo schema XML Foglia.xsd
:
<xs:schema
xmlns:sc1="http://www.elementiBase.bindings.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.elementiBase.bindings.com" >
<xs:import namespace="http://www.w3.org/2001/XMLSchema" />
<xs:element name="CaratteristicheFoglie">
<xs:complexType>
<xs:sequence>
<xs:element name="Tipo" type="xs:string" />
<xs:element name="Forma" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Lo modifichiamo aggiornando il namespace (xmlns e JAXB version) e inserendo le annotazioni necessarie a modificare il nome del package, ottenendo:
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xs:schema
xmlns:sc1="http://www.elementiBase.bindings.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
jaxb:version="2.0"
targetNamespace="http://www.elementiBase.bindings.com" >
<xs:import namespace="http://www.w3.org/2001/XMLSchema" />
<xs:annotation>
<xs:appinfo>
<jaxb:schemaBindings>
<jaxb:package name="com.bindings.elementiModificati"/>
</jaxb:schemaBindings>
</xs:appinfo>
</xs:annotation>
<xs:element name="CaratteristicheFoglie">
<xs:complexType>
<xs:sequence>
<xs:element name="Tipo" type="xs:string" />
<xs:element name="Forma" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
A questo punto è possibile compilare utilizzando il binding compiler xjc, come mostrato nel precedente articolo.
Come mostrato nello screenshot, il nome del package di appartenenza della classe CaratteristicheFoglie non è più com.bindings.elementiBase
, ma è divenuto com.bindings.elementiModificati
. Occorre comunque tener presente che la modifica è a livello di schema, ciò significa che la modifica effettuata al nome del package si propaga alle altre classi generate appartenenti al package di partenza, con il risultato di non avere più il package con il nome originario. L’alternativa, ove possibile, è compilare i singoli schemi XML. Altra possibilità è quella di unire diversi package in uno solo, sempreché ciò non generi conflitti.
Customizzazione mediante file di binding
La customizzazione mediante file di binding avviene creando un apposito file che contiene le dichiarazioni necessarie a modificare il comportamento standard. Generalmente le dichiarazioni assumono la seguente forma:
<jxb:bindings schemaLocation = "xs:anyURI">
<jxb:bindings node = "xs:string">
<binding declaration>
<jxb:bindings>
</jxb:bindings>
schemaLocation indica un riferimento (che può essere assoluto o relativo) allo schema XML da modificare. node invece indica il percorso, espresso mediante il linguaggio XPath 1.0, necessario per identificare il nodo dello schema cui applicare le regole di customizzazione. Si mostrano di seguito due esempi di riferimento a file remoti. Da notare che nel secondo esempio si fa riferimento a file distribuiti su internet (pertanto per poter effettuare il mapping sarà necessaria la connessione ad internet).
<bindings schemaLocation="Fusto.xsd" node="/xs:schema">
<bindings schemaLocation="http://schemas.org.net/schemaXML.xsd">
L’estensione del file di binding realizzato non ha peso, può anche essere .xml
, ma solitamente si usa l’estensione .xjc
. Il file di binding viene passato al compilatore xjc
tramite l’opzione -b <file>, come riportato di seguito:
>> xjc -b <file> <schema>
Dove <file> indica il nome del file di binding e <schema> il nome dello schema XML. E’ possibile passare più di un file, ricordando di farli precedere tutti dall’opzione –b.
Segue un esempio nel quale si realizza un file di binding (bindings.xjb
) che, dato lo schema XML Foglia.xsd
(con o senza le modifiche apportate nel precedente esempio, cambierà solo il nome del package generato), si propone di modificare il nome della classe generata. Posizioniamo questo file nella stessa cartella dove è stato posizionato il file Foglia.xsd
.
<bindings version="2.0"
xmlns="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:sc1="http://www.elementiBase.bindings.com" >
<bindings schemaLocation="file:./Foglia.xsd">
<bindings node=".//xs:element[@name='CaratteristicheFoglie']">
<class name="ClasseFoglia"/>
</bindings>
</bindings>
</bindings>
Nell’esempio presentato si è scelto di utilizzare lo schema XML con le annotazioni inline. Come è possibile vedere nel seguente screenshot, l’esecuzione del comando xjc
con l’opzione –b ha consentito di modificare il comportamento prestabilito, modificando il nome della classe generata secondo le istruzioni fornite nel file bindings.xjb
. É inoltre possibile vedere che si sono sommati gli effetti della annotazioni inline (modifica del nome del package) e del file esterno (modifica del nome della classe).
Vantaggi della customizzazione mediante file di binding
Si è già accennato al fatto che utilizzare un file esterno può presentare vantaggi significativi, pur presentando la complicazione di dovere realizzare un file remoto e di dovere indicare tramite il linguaggio XPath il nodo dove apportare la modifica. Vengono di seguito riassunti i vantaggi:
- Modifiche centralizzate
- Gli schemi XML non devono essere modificati
- Possibilità di customizzare schemi remoti
In definitiva si facilita il riuso e si abilita la customizzazione di schemi non modificabili, ad esempio in quanto disponibili solo tramite internet o soggetti a particolari restrizioni. Inoltre è possibile evitare di disperdere le modifiche nei vari schemi XML, riducendo in definitiva gli sforzi necessari in fase di sviluppo e di manutenzione.
Nella prossima parte entreremo nel merito dei problemi legati a possibili collisioni, e vedremo come risolverli.
Risoluzione di collisioni, un esempio approfondito
Scopo di questa sezione è familiarizzare con le tecniche di risoluzione dei conflitti. Le collisioni possono avvenire per diversi motivi, legati alle modalità con le quali avviene il mapping XML a Java. Pertanto, verranno introdotti degli schemi XML pensati appositamente per sollevare alcuni tipi di collisioni, di modo da mostrare come si manifestano e come risolverli.
Di seguito si riportano gli schemi XML e l’organizzazione dei file nelle rispettive cartelle:
JAXBesempi Aereo All Aereo.xsd Schemi Ala.xsd Carrello.xsd Datigenerali.xsd Elementicarrello.xsd Fusoliera.xsd Motore.xsd
Schemi XML:
Aereo.xsd
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xs:schema
xmlns:sc3="http://www.aereo.bindings.com"
xmlns:sc2="http://www.elementiComposti.bindings.com"
xmlns:sc1="http://www.elementiBase.bindings.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.aereo.bindings.com">
<xs:import namespace="http://www.elementiBase.bindings.com" schemaLocation="../Schemi/Ala.xsd" />
<xs:import namespace="http://www.elementiComposti.bindings.com" schemaLocation="../Schemi/Carrello.xsd" />
<xs:import namespace="http://www.elementiBase.bindings.com" schemaLocation="../Schemi/Motore.xsd" />
<xs:import namespace="http://www.elementiComposti.bindings.com" schemaLocation="../Schemi/DatiGenerali.xsd" />
<xs:element name="Aereo">
<xs:complexType>
<xs:sequence>
<xs:element ref="sc2:Tipo" />
<xs:element ref="sc2:Struttura" />
<xs:element ref="sc1:Turbina" />
<xs:element ref="sc1:Profilo_alare" />
<xs:element ref="sc2:ModelloCarrello" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Ala.xsd
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xs:schema
xmlns:sc1="http://www.elementiBase.bindings.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.elementiBase.bindings.com">
<xs:import namespace="http://www.w3.org/2001/XMLSchema" />
<xs:element name="Profiloalare">
<xs:complexType>
<xs:sequence>
<xs:element name="BordoAttacco" type="xs:string" />
<xs:element name="BordoUscita" type="xs:string" />
<xs:element name="Spessore" type="xs:int" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Profilo_alare">
<xs:complexType>
<xs:sequence>
<xs:element name="BordoAttacco" type="xs:string" />
<xs:element name="BordoUscita" type="xs:string" />
<xs:element name="Spessore" type="xs:int" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Carrello.xsd
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xs:schema
xmlns:sc2="http://www.elementiComposti.bindings.com"
xmlns:sc1="http://www.elementiBase.bindings.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.elementiComposti.bindings.com">
<xs:import namespace="http://www.elementiBase.bindings.com" schemaLocation="ElementiCarrello.xsd"/>
<xs:element name="ModelloCarrello">
<xs:complexType>
<xs:sequence>
<xs:element ref="sc1:Tipologia" />
<xs:element ref="sc1:Ruote" />
<xs:element ref="sc1:Freno" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Datigenerali.xsd
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xs:schema
xmlns:sc2="http://www.elementiComposti.bindings.com"
xmlns:sc1="http://www.elementiBase.bindings.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.elementiComposti.bindings.com">
<xs:import namespace="http://www.elementiBase.bindings.com" schemaLocation="Fusoliera.xsd"/>
<xs:element name="Tipo">
<xs:complexType>
<xs:sequence>
<xs:element name="Nome" type="xs:string" />
<xs:element name="Tipologia" type="xs:string" />
<xs:element name="Versione" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Struttura">
<xs:complexType>
<xs:sequence>
<xs:element ref="sc1:Tipo" />
<xs:element name="PesoMax" type="xs:int" />
<xs:element name="PesoMin" type="xs:int" />
<xs:element name="AperturaAlare" type="xs:int" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Elementicarrello.xsd
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xs:schema
xmlns:sc1="http://www.elementiBase.bindings.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.elementiBase.bindings.com">
<xs:import namespace="http://www.w3.org/2001/XMLSchema" />
<xs:element name="Freno">
<xs:complexType>
<xs:sequence>
<xs:element name="Tipologia" type="xs:string" />
<xs:element name="Materiale" type="xs:string" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Ruote">
<xs:complexType>
<xs:sequence>
<xs:element name="Numero" type="xs:int" />
<xs:element name="Diametro" type="xs:int" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Tipologia">
<xs:complexType>
<xs:sequence>
<xs:element name="Nome" type="xs:string" />
</xs:sequence>
<xs:attribute name="Nome" type="xs:string" />
</xs:complexType>
</xs:element>
</xs:schema>
Fusoliera.xsd
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xs:schema
xmlns:sc1="http://www.elementiBase.bindings.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.elementiBase.bindings.com">
<xs:import namespace="http://www.w3.org/2001/XMLSchema" />
<xs:element name="Tipo">
<xs:complexType>
<xs:sequence>
<xs:element name="Tipologia" type="xs:string" />
<xs:element name="Lunghezza" type="xs:int" />
<xs:element name="Larghezza" type="xs:int" />
<xs:element name="Passeggeri" type="xs:int" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Motore.xsd
<?xml version="1.0" encoding="ISO-8859-1" ?>
<xs:schema
xmlns:sc1="http://www.elementiBase.bindings.com"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
targetNamespace="http://www.elementiBase.bindings.com">
<xs:import namespace="http://www.w3.org/2001/XMLSchema" />
<xs:element name="Turbina">
<xs:complexType>
<xs:sequence>
<xs:element name="Tipo" type="xs:string" />
<xs:element name="Applicazione" type="xs:string" />
<xs:element name="Potenza" type="xs:int" />
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="turbina">
<xs:complexType>
<xs:sequence>
<xs:element name="tipo" type="xs:string" />
<xs:element name="applicazione" type="xs:string" />
<xs:element name="potenza" type="xs:int" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>
Realizzati i file nelle cartelle indicate, dopo aver lanciato una console ed essersi spostati nella directory All
, è possibile lanciare il binding compiler sullo schema XML Aereo.xsd
:
Il compilatore ci informa che, a causa di un conflitto, non è stato possibile effettuare il mapping verso Java. Vengono fornite indicazioni sul conflitto individuato, consistente in una ripetizione della proprietà Nome
nello schema ElementiCarrello.xsd
. In effetti, in questo schema il termine Nome
è ripetuto, prima per un elemento e successivamente per un attributo, pertanto il mapping violerebbe l’unicità degli identificatori in Java. Per risolvere tale conflitto, procediamo alla creazione di un file di binding, chiamato bindings.xjb
, che posizioneremo nella cartella All
:
<?xml version="1.0" encoding="ISO-8859-1"?>
<bindings version="1.0"
xmlns="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:sc2="http://www.elementiComposti.bindings.com"
xmlns:sc1="http://www.elementiBase.bindings.com" >
<bindings schemaLocation="file:../Schemi/ElementiCarrello.xsd">
<bindings node=".//xs:element[@name='Tipologia']//xs:complexType//xs:attribute[@name='Nome']">
<property name="NomeAttributo"/>
</bindings>
</bindings>
</bindings>
Notiamo in primo luogo che, anche per venire incontro alle successive modifiche, si sono riportate le informazioni complete sul namespace (xmlns:sc1
e xmlns:sc2
). Successivamente osserviamo come indicare il percorso per l’importazione dello schema in cui occorre effettuare la modifica. Il percorso presentato nell’esempio è relativo. I due punti iniziali (file:../
) indicano la cartella gerarchicamente superiore, ossia la cartella Aereo
. Infine osserviamo la modifica richiesta. In primo luogo occorre specificare perfettamente il nodo in cui eseguire la modifica, osservare pertanto con attenzione l’applicazione dell’Xpath per identificare il nodo in questione. Oltre agli elementi occorre specificare il namespace. Successivamente possiamo osservare la modifica richiesta. Si sta chiedendo di rinominare l’attributo in NomeAttributo
, di modo da rendere univoci gli identificativi. Lanciamo nuovamente il binding compiler, questa volta passando anche il file di binding tramite l’opzione –b <file>
:
Come è possibile notare, il problema precedente è risolto... ma ne sono sorti altri! Affrontiamo un problema per volta.
Il primo ostacolo riguarda lo schema Motore.xsd
. Il compilatore ci informa che vi è già un’interfaccia definita come Turbina
. Osservando gli schemi, sarà possibile notare che vi sono si due elementi simili, ma uno inizia con una lettera maiuscola e uno con la minuscola. Il problema è che in sistemi operativi come Windows non c’è la possibilità di discernere i due nomi dei file che verrebbero generati, per cui il compilatore ci segnala il problema. Osservando più in dettaglio lo schema, si può notare come i due elementi siano in fondo ridondanti, questo può avvenire perché l’autore degli schemi aveva un’esigenza specifica o perché prevedeva così di poter gestire meglio il comportamento degli utenti. Sta di fatto che se vogliamo compilare questo schema dobbiamo intervenire. Come detto in precedenza, il file esterno ci permette di centralizzare le modifiche apportate, aggiungiamo pertanto al file quest’ulteriore dichiarazione, stando attenti a come innestarla nel file di partenza:
<bindings schemaLocation="file:../Schemi/Motore.xsd">
<bindings node=".//xs:element[@name='turbina']">
<class name="turbinaMinusc"/>
</bindings>
</bindings>
Possiamo ora ricompilare:
Il conflitto relativo agli elementi nello schema Motore.xsd
è stato risolto e possiamo passare ad analizzare il conflitto successivo.
Questa volta il problema è nel file Ala.xsd
, relativo agli elementi Profiloalare
e Profilo_alare
. Ancora una volta, questi due elementi vengono mappati in modo analogo. Ciò è dovuto al fatto che di default il carattere underscore è trattato come separatore generico e non come carattere. Più in dettaglio, il mapping di un generico elemento Elem_ento
produce di default una classe ElemEnto
. In tal caso, possiamo modificare il comportamento di default a livello globale, agendo sull’opzione
underscoreBinding = "asWordSeparator" | "asCharInWord"
Siamo interessati ad avere nel mapping anche l’underscore, per cui nel file di binding aggiungeremo la seguente dichiarazione:
<globalBindings
underscoreBinding="asCharInWord">
</globalBindings>
Il conflitto è risolto e questa volta il binding compiler è in grado di generare le classi Java. Riportiamo per completezza il file di binding con tutte le modifiche effettuate:
<?xml version="1.0" encoding="ISO-8859-1"?>
<bindings version="1.0"
xmlns="http://java.sun.com/xml/ns/jaxb"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:sc2="http://www.elementiComposti.bindings.com"
xmlns:sc1="http://www.elementiBase.bindings.com">
<globalBindings underscoreBinding="asCharInWord" >
</globalBindings>
<bindings schemaLocation="file:../Schemi/ElementiCarrello.xsd">
<bindings node=".//xs:element[@name='Tipologia']//xs:complexType//xs:attribute[@name='Nome']">
<property name="NomeAttributo"/>
</bindings>
</bindings>
<bindings schemaLocation="file:../Schemi/Motore.xsd">
<bindings node=".//xs:element[@name='turbina']">
<class name="turbinaMinusc"/>
</bindings>
</bindings>
</bindings>
Negli schemi erano presenti alcune delle situazioni più comuni che possono portare alla generazione dei conflitti. Altri problemi possono nascere dall’inavvertito utilizzo di nomi riservati in Java (ad esempio Class
), ma l’approccio alla risoluzione può restare invariato.
Conclusioni
In questo articolo si è mostrato come e perché modificare il comportamento di default del binding compiler. Scopo finale è risparmiare tempo e conseguentemente sforzi di sviluppo e/o manutenzione attraverso il riuso di schemi XML già esistenti che possono risultare utili alle nostre attività con modifiche minime. Per questo fine, il framework JAXB mette a disposizione gli strumenti per customizzare il mapping XML a Java. Si sono pertanto descritti gli strumenti messi a disposizione dal framework e le modalità con cui utilizzarli, mostrando al contempo come possa rivelarsi opportuno utilizzare un file esterno per raccogliere le dichiarazioni di binding, invece che modificare direttamente gli schemi XML di partenza, il tutto corredato da esempi itroduttivi.
Abbiamo visto infine nell’ultima sezione che la customizzazione può rivelarsi necessaria per risolvere dei conflitti. Si sono mostrati dei conflitti tipici, presentando delle soluzioni utili per risolverli. Gli stessi conflitti possono presentarsi utilizzando altri strumenti come il wsdl2java del framework Apache CXF (che sarà oggetto di uno dei prossimi articoli), o il wsconsume appartenente al JAX-WS Tools, l’approccio alla risoluzione del problema sarà lo stesso. In alcuni casi inoltre i messaggi di errore generati dagli strumenti utilizzati possono risultare non sufficienti ad individuare il/i problemi specifici, per cui può risultare opportuno servirsi del binding compiler xjc per individuare l’origine dei conflitti e risolverli agevolmente, passando successivamente al tool usato il file di binding realizzato.