blob: addf20b820da3d336ea930e8c269fe7d94cf796f [file] [log] [blame]
<!--
/***************************************************************************************************************************
* Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
* specific language governing permissions and limitations under the License.
***************************************************************************************************************************/
-->
XML-Schema Support
<p>
Juneau provides the {@link oaj.xmlschema.XmlSchemaSerializer} class for generating XML-Schema
documents that describe the output generated by the {@link oaj.xml.XmlSerializer} class.
This class shares the same properties as <c>XmlSerializer</c>.
Since the XML output differs based on settings on the XML serializer class, the XML-Schema serializer
class must have the same property values as the XML serializer class it's describes.
To help facilitate creating an XML Schema serializer with the same properties as the corresponding
XML serializer, the {@link oaj.xml.XmlSerializer#getSchemaSerializer()} method
has been added.
</p>
<p>
XML-Schema requires a separate file for each namespace.
Unfortunately, does not mesh well with the Juneau serializer architecture which serializes to single writers.
To get around this limitation, the schema serializer will produce a single output, but with multiple
schema documents separated by the null character (<js>'\u0000'</js>) to make it simple to split apart.
</p>
<p>
Lets start with an example where everything is in the same namespace.
We'll use the classes from before, but remove the references to namespaces.
Since we have not defined a default namespace, everything is defined under the default Juneau namespace.
</p>
<h5 class='figure'>Sample Beans</h5>
<p class='bpcode w800'>
<ja>@Bean</ja>(typeName=<js>"person"</js>)
<jk>public class</jk> Person {
<jc>// Bean properties</jc>
<jk>public</jk> String <jf>name</jf>;
<ja>@Swap</ja>(TemporalCalendarSwap.IsoInstant.<jk>class</jk>) <jk>public</jk> Calendar <jf>birthDate</jf>;
<jk>public</jk> List&lt;Address&gt; <jf>addresses</jf>;
<jc>// Getters/setters omitted</jc>
}
<ja>@Bean</ja>(typeName=<js>"address"</js>)
<jk>public class</jk> Address {
<jc>// Bean properties</jc>
<jk>public</jk> String <jf>street</jf>, <jf>city</jf>;
<jk>public</jk> StateEnum <jf>state</jf>;
<jk>public int</jk> <jf>zip</jf>;
<jk>public boolean</jk> <jf>isCurrent</jf>;
<jc>// Getters/setters omitted</jc>
}
</p>
<p>
The code for creating our POJO model and generating XML Schema is shown below:
</p>
<p class='bpcode w800'>
<jc>// Create a new serializer with readable output.</jc>
XmlSerializer s = XmlSerializer.<jsm>create</jsm>()
.ws()
.ns()
.sq()
.addNamespaceUrisToRoot(<jk>true</jk>)
.build();
<jc>// Create the equivalent schema serializer.</jc>
XmlSchemaSerializer ss = s.getSchemaSerializer();
<jc>// Get the XML Schema corresponding to the XML generated above.</jc>
String xmlSchema = ss.serialize(<jk>new</jk> Person());
</p>
<h5 class='figure'>XML-Schema results</h5>
<p class='bpcode w800'>
<xt>&lt;schema</xt>
<xa>xmlns</xa>=<xs>'http://www.w3.org/2001/XMLSchema'</xs>
<xa>targetNamespace</xa>=<xs>'http://www.apache.org/2013/Juneau'</xs>
<xa>elementFormDefault</xa>=<xs>'qualified'</xs>
<xa>xmlns:juneau</xa>=<xs>'http://www.apache.org/2013/Juneau'</xs><xt>&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'person'</xs> <xa>_type</xa>=<xs>'juneau:org.apache.juneau.examples.addressbook.Person'</xs><xt>/&gt;</xt>
<xt>&lt;complexType</xt> <xa>name</xa>=<xs>'org.apache.juneau.examples.addressbook.Person'</xs><xt>&gt;</xt>
<xt>&lt;sequence&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'name'</xs> <xa>_type</xa>=<xs>'string'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'birthDate'</xs> <xa>_type</xa>=<xs>'juneau:java.util.Calendar'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'addresses'</xs> <xa>_type</xa>=<xs>'juneau:java.util.LinkedList_x003C_org.apache.juneau.examples.addressbook.Address_x003E_'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/&gt;</xt>
<xt>&lt;/sequence&gt;</xt>
<xt>&lt;/complexType&gt;</xt>
<xt>&lt;complexType</xt> <xa>name</xa>=<xs>'java.util.Calendar'</xs><xt>&gt;</xt>
<xt>&lt;sequence&gt;</xt>
<xt>&lt;any</xt> <xa>processContents</xa>=<xs>'skip'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/&gt;</xt>
<xt>&lt;/sequence&gt;</xt>
<xt>&lt;/complexType&gt;</xt>
<xt>&lt;complexType</xt> <xa>name</xa>=<xs>'java.util.LinkedList_x003C_org.apache.juneau.examples.addressbook.Address_x003E_'</xs><xt>&gt;</xt>
<xt>&lt;sequence&gt;</xt>
<xt>&lt;choice</xt> <xa>minOccurs</xa>=<xs>'0'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs><xt>&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'address'</xs> <xa>_type</xa>=<xs>'juneau:org.apache.juneau.examples.addressbook.Address'</xs><xt>/&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'null'</xs> <xa>_type</xa>=<xs>'string'</xs><xt>/&gt;</xt>
<xt>&lt;/choice&gt;</xt>
<xt>&lt;/sequence&gt;</xt>
<xt>&lt;/complexType&gt;</xt>
<xt>&lt;complexType</xt> <xa>name</xa>=<xs>'org.apache.juneau.examples.addressbook.Address'</xs><xt>&gt;</xt>
<xt>&lt;sequence&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'street'</xs> <xa>_type</xa>=<xs>'string'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'city'</xs> <xa>_type</xa>=<xs>'string'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'state'</xs> <xa>_type</xa>=<xs>'string'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'zip'</xs> <xa>_type</xa>=<xs>'integer'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'isCurrent'</xs> <xa>_type</xa>=<xs>'boolean'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/&gt;</xt>
<xt>&lt;/sequence&gt;</xt>
<xt>&lt;/complexType&gt;</xt>
<xt>&lt;/schema&gt;</xt>
</p>
<p>
Now if we add in some namespaces, we'll see how multiple namespaces are handled.
</p>
<h5 class='figure'>Sample Beans with multiple namespaces</h5>
<p class='bpcode w800'>
<ja>@Xml</ja>(prefix=<js>"per"</js>)
<ja>@Bean</ja>(typeName=<js>"person"</js>)
<jk>public class</jk> Person {
<jc>// Bean properties</jc>
<jk>public</jk> String <jf>name</jf>;
<ja>@Swap</ja>(TemporalCalendarSwap.IsoInstant.<jk>class</jk>) <jk>public</jk> Calendar <jf>birthDate</jf>;
<jk>public</jk> List&lt;Address&gt; <jf>addresses</jf>;
<jc>// Getters/setters omitted</jc>
}
<ja>@Xml</ja>(prefix=<js>"addr"</js>)
<ja>@Bean</ja>(typeName=<js>"address"</js>)
<jk>public class</jk> Address {
<jc>// Bean properties</jc>
<ja>@Xml</ja>(prefix=<js>"mail"</js>) <jk>public</jk> String <jf>street</jf>, <jf>city</jf>;
<ja>@Xml</ja>(prefix=<js>"mail"</js>) <jk>public</jk> StateEnum <jf>state</jf>;
<ja>@Xml</ja>(prefix=<js>"mail"</js>) <jk>public int</jk> <jf>zip</jf>;
<jk>public boolean</jk> <jf>isCurrent</jf>;
<jc>// Getters/setters omitted</jc>
}
</p>
<p>
The schema consists of 4 documents separated by a <js>'\u0000'</js> character.
</p>
<h5 class='figure'>XML-Schema results</h5>
<p class='bpcode w800'>
<xt>&lt;schema</xt>
<xa>xmlns</xa>=<xs>'http://www.w3.org/2001/XMLSchema'</xs>
<xa>targetNamespace</xa>=<xs>'http://www.apache.org/2013/Juneau'</xs>
<xa>elementFormDefault</xa>=<xs>'qualified'</xs>
<xa>xmlns:juneau</xa>=<xs>'http://www.apache.org/2013/Juneau'</xs>
<xa>xmlns:per</xa>=<xs>'http://www.apache.org/person/'</xs>
<xa>xmlns:addr</xa>=<xs>'http://www.apache.org/address/'</xs>
<xa>xmlns:mail</xa>=<xs>'http://www.apache.org/mail/'</xs><xt>&gt;</xt>
<xt>&lt;import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/person/'</xs> <xa>schemaLocation</xa>=<xs>'per.xsd'</xs><xt>/&gt;</xt>
<xt>&lt;import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/address/'</xs> <xa>schemaLocation</xa>=<xs>'addr.xsd'</xs><xt>/&gt;</xt>
<xt>&lt;import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/mail/'</xs> <xa>schemaLocation</xa>=<xs>'mail.xsd'</xs><xt>/&gt;</xt>
<xt>&lt;complexType</xt> <xa>name</xa>=<xs>'int'</xs><xt>&gt;</xt>
<xt>&lt;simpleContent&gt;</xt>
<xt>&lt;extension</xt> <xa>base</xa>=<xs>'integer'</xs><xt>/&gt;</xt>
<xt>&lt;/simpleContent&gt;</xt>
<xt>&lt;/complexType&gt;</xt>
<xt>&lt;complexType</xt> <xa>name</xa>=<xs>'java.lang.String'</xs><xt>&gt;</xt>
<xt>&lt;simpleContent&gt;</xt>
<xt>&lt;extension</xt> <xa>base</xa>=<xs>'string'</xs><xt>/&gt;</xt>
<xt>&lt;/simpleContent&gt;</xt>
<xt>&lt;/complexType&gt;</xt>
<xt>&lt;complexType</xt> <xa>name</xa>=<xs>'java.net.URI'</xs><xt>&gt;</xt>
<xt>&lt;simpleContent&gt;</xt>
<xt>&lt;extension</xt> <xa>base</xa>=<xs>'string'</xs><xt>/&gt;</xt>
<xt>&lt;/simpleContent&gt;</xt>
<xt>&lt;/complexType&gt;</xt>
<xt>&lt;complexType</xt> <xa>name</xa>=<xs>'java.util.Calendar'</xs><xt>&gt;</xt>
<xt>&lt;sequence&gt;</xt>
<xt>&lt;any</xt> <xa>processContents</xa>=<xs>'skip'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/&gt;</xt>
<xt>&lt;/sequence&gt;</xt>
<xt>&lt;/complexType&gt;</xt>
<xt>&lt;complexType</xt> <xa>name</xa>=<xs>'java.util.LinkedList_x003C_org.apache.juneau.examples.addressbook.Address_x003E_'</xs><xt>&gt;</xt>
<xt>&lt;sequence&gt;</xt>
<xt>&lt;choice</xt> <xa>minOccurs</xa>=<xs>'0'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs><xt>&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'address'</xs> <xa>_type</xa>=<xs>'addr:org.apache.juneau.examples.addressbook.Address'</xs><xt>/&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'null'</xs> <xa>_type</xa>=<xs>'string'</xs><xt>/&gt;</xt>
<xt>&lt;/choice&gt;</xt>
<xt>&lt;/sequence&gt;</xt>
<xt>&lt;/complexType&gt;</xt>
<xt>&lt;complexType</xt> <xa>name</xa>=<xs>'boolean'</xs><xt>&gt;</xt>
<xt>&lt;simpleContent&gt;</xt>
<xt>&lt;extension</xt> <xa>base</xa>=<xs>'boolean'</xs><xt>/&gt;</xt>
<xt>&lt;/simpleContent&gt;</xt>
<xt>&lt;/complexType&gt;</xt>
<xt>&lt;/schema&gt;</xt>
[\u0000]
<xt>&lt;schema</xt>
<xa>xmlns</xa>=<xs>'http://www.w3.org/2001/XMLSchema'</xs>
<xa>targetNamespace</xa>=<xs>'http://www.apache.org/person/'</xs>
<xa>elementFormDefault</xa>=<xs>'qualified'</xs>
<xa>attributeFormDefault</xa>=<xs>'qualified'</xs>
<xa>xmlns:juneau</xa>=<xs>'http://www.apache.org/2013/Juneau'</xs>
<xa>xmlns:per</xa>=<xs>'http://www.apache.org/person/'</xs>
<xa>xmlns:addr</xa>=<xs>'http://www.apache.org/address/'</xs>
<xa>xmlns:mail</xa>=<xs>'http://www.apache.org/mail/'</xs><xt>&gt;</xt>
<xt>&lt;import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/2013/Juneau'</xs> <xa>schemaLocation</xa>=<xs>'juneau.xsd'</xs><xt>/&gt;</xt>
<xt>&lt;import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/address/'</xs> <xa>schemaLocation</xa>=<xs>'addr.xsd'</xs><xt>/&gt;</xt>
<xt>&lt;import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/mail/'</xs> <xa>schemaLocation</xa>=<xs>'mail.xsd'</xs><xt>/&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'person'</xs> <xa>_type</xa>=<xs>'per:org.apache.juneau.examples.addressbook.Person'</xs><xt>/&gt;</xt>
<xt>&lt;complexType</xt> <xa>name</xa>=<xs>'org.apache.juneau.examples.addressbook.Person'</xs><xt>&gt;</xt>
<xt>&lt;sequence&gt;</xt>
<xt>&lt;any</xt> <xa>minOccurs</xa>=<xs>'0'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs><xt>/&gt;</xt>
<xt>&lt;/sequence&gt;</xt>
<xt>&lt;attribute</xt> <xa>name</xa>=<xs>'uri'</xs> <xa>_type</xa>=<xs>'string'</xs><xt>/&gt;</xt>
<xt>&lt;/complexType&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'name'</xs> <xa>_type</xa>=<xs>'juneau:java.lang.String'</xs><xt>/&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'birthDate'</xs> <xa>_type</xa>=<xs>'juneau:java.util.Calendar'</xs><xt>/&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'addresses'</xs> <xa>_type</xa>=<xs>'juneau:java.util.LinkedList_x003C_org.apache.juneau.examples.addressbook.Address_x003E_'</xs><xt>/&gt;</xt>
<xt>&lt;/schema&gt;</xt>
[\u0000]
<xt>&lt;schema</xt>
<xa>xmlns</xa>=<xs>'http://www.w3.org/2001/XMLSchema'</xs>
<xa>targetNamespace</xa>=<xs>'http://www.apache.org/address/'</xs>
<xa>elementFormDefault</xa>=<xs>'qualified'</xs>
<xa>attributeFormDefault</xa>=<xs>'qualified'</xs>
<xa>xmlns:juneau</xa>=<xs>'http://www.apache.org/2013/Juneau'</xs>
<xa>xmlns:per</xa>=<xs>'http://www.apache.org/person/'</xs>
<xa>xmlns:addr</xa>=<xs>'http://www.apache.org/address/'</xs>
<xa>xmlns:mail</xa>=<xs>'http://www.apache.org/mail/'</xs><xt>&gt;</xt>
<xt>&lt;import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/2013/Juneau'</xs> <xa>schemaLocation</xa>=<xs>'juneau.xsd'</xs><xt>/&gt;</xt>
<xt>&lt;import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/person/'</xs> <xa>schemaLocation</xa>=<xs>'per.xsd'</xs><xt>/&gt;</xt>
<xt>&lt;import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/mail/'</xs> <xa>schemaLocation</xa>=<xs>'mail.xsd'</xs><xt>/&gt;</xt>
<xt>&lt;complexType</xt> <xa>name</xa>=<xs>'org.apache.juneau.examples.addressbook.Address'</xs><xt>&gt;</xt>
<xt>&lt;sequence&gt;</xt>
<xt>&lt;any</xt> <xa>minOccurs</xa>=<xs>'0'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs><xt>/&gt;</xt>
<xt>&lt;/sequence&gt;</xt>
<xt>&lt;/complexType&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'isCurrent'</xs> <xa>_type</xa>=<xs>'juneau:boolean'</xs><xt>/&gt;</xt>
<xt>&lt;/schema&gt;</xt>
[\u0000]
<xt>&lt;schema</xt>
<xa>xmlns</xa>=<xs>'http://www.w3.org/2001/XMLSchema'</xs>
<xa>targetNamespace</xa>=<xs>'http://www.apache.org/mail/'</xs>
<xa>elementFormDefault</xa>=<xs>'qualified'</xs>
<xa>attributeFormDefault</xa>=<xs>'qualified'</xs>
<xa>xmlns:juneau</xa>=<xs>'http://www.apache.org/2013/Juneau'</xs>
<xa>xmlns:per</xa>=<xs>'http://www.apache.org/person/'</xs>
<xa>xmlns:addr</xa>=<xs>'http://www.apache.org/address/'</xs>
<xa>xmlns:mail</xa>=<xs>'http://www.apache.org/mail/'</xs><xt>&gt;</xt>
<xt>&lt;import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/2013/Juneau'</xs> <xa>schemaLocation</xa>=<xs>'juneau.xsd'</xs><xt>/&gt;</xt>
<xt>&lt;import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/person/'</xs> <xa>schemaLocation</xa>=<xs>'per.xsd'</xs><xt>/&gt;</xt>
<xt>&lt;import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/address/'</xs> <xa>schemaLocation</xa>=<xs>'addr.xsd'</xs><xt>/&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'street'</xs> <xa>_type</xa>=<xs>'juneau:java.lang.String'</xs><xt>/&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'city'</xs> <xa>_type</xa>=<xs>'juneau:java.lang.String'</xs><xt>/&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'state'</xs> <xa>_type</xa>=<xs>'juneau:java.lang.String'</xs><xt>/&gt;</xt>
<xt>&lt;element</xt> <xa>name</xa>=<xs>'zip'</xs> <xa>_type</xa>=<xs>'juneau:int'</xs><xt>/&gt;</xt>
<xt>&lt;/schema&gt;</xt>
</p>
<p>
For convenience, the {@link oaj.xmlschema.XmlSchemaSerializer
#getValidator(SerializerSession,Object)} method is provided to create a
{@link javax.xml.validation.Validator} using the input from the serialize method.
</p>