| <!-- |
| /*************************************************************************************************************************** |
| * 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<Address> <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><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>></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'person'</xs> <xa>_type</xa>=<xs>'juneau:org.apache.juneau.examples.addressbook.Person'</xs><xt>/></xt> |
| <xt><complexType</xt> <xa>name</xa>=<xs>'org.apache.juneau.examples.addressbook.Person'</xs><xt>></xt> |
| <xt><sequence></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'name'</xs> <xa>_type</xa>=<xs>'string'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'birthDate'</xs> <xa>_type</xa>=<xs>'juneau:java.util.Calendar'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> |
| <xt><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>/></xt> |
| <xt></sequence></xt> |
| <xt></complexType></xt> |
| <xt><complexType</xt> <xa>name</xa>=<xs>'java.util.Calendar'</xs><xt>></xt> |
| <xt><sequence></xt> |
| <xt><any</xt> <xa>processContents</xa>=<xs>'skip'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> |
| <xt></sequence></xt> |
| <xt></complexType></xt> |
| <xt><complexType</xt> <xa>name</xa>=<xs>'java.util.LinkedList_x003C_org.apache.juneau.examples.addressbook.Address_x003E_'</xs><xt>></xt> |
| <xt><sequence></xt> |
| <xt><choice</xt> <xa>minOccurs</xa>=<xs>'0'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs><xt>></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'address'</xs> <xa>_type</xa>=<xs>'juneau:org.apache.juneau.examples.addressbook.Address'</xs><xt>/></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'null'</xs> <xa>_type</xa>=<xs>'string'</xs><xt>/></xt> |
| <xt></choice></xt> |
| <xt></sequence></xt> |
| <xt></complexType></xt> |
| <xt><complexType</xt> <xa>name</xa>=<xs>'org.apache.juneau.examples.addressbook.Address'</xs><xt>></xt> |
| <xt><sequence></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'street'</xs> <xa>_type</xa>=<xs>'string'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'city'</xs> <xa>_type</xa>=<xs>'string'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'state'</xs> <xa>_type</xa>=<xs>'string'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'zip'</xs> <xa>_type</xa>=<xs>'integer'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'isCurrent'</xs> <xa>_type</xa>=<xs>'boolean'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> |
| <xt></sequence></xt> |
| <xt></complexType></xt> |
| <xt></schema></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<Address> <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><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>></xt> |
| <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/person/'</xs> <xa>schemaLocation</xa>=<xs>'per.xsd'</xs><xt>/></xt> |
| <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/address/'</xs> <xa>schemaLocation</xa>=<xs>'addr.xsd'</xs><xt>/></xt> |
| <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/mail/'</xs> <xa>schemaLocation</xa>=<xs>'mail.xsd'</xs><xt>/></xt> |
| <xt><complexType</xt> <xa>name</xa>=<xs>'int'</xs><xt>></xt> |
| <xt><simpleContent></xt> |
| <xt><extension</xt> <xa>base</xa>=<xs>'integer'</xs><xt>/></xt> |
| <xt></simpleContent></xt> |
| <xt></complexType></xt> |
| <xt><complexType</xt> <xa>name</xa>=<xs>'java.lang.String'</xs><xt>></xt> |
| <xt><simpleContent></xt> |
| <xt><extension</xt> <xa>base</xa>=<xs>'string'</xs><xt>/></xt> |
| <xt></simpleContent></xt> |
| <xt></complexType></xt> |
| <xt><complexType</xt> <xa>name</xa>=<xs>'java.net.URI'</xs><xt>></xt> |
| <xt><simpleContent></xt> |
| <xt><extension</xt> <xa>base</xa>=<xs>'string'</xs><xt>/></xt> |
| <xt></simpleContent></xt> |
| <xt></complexType></xt> |
| <xt><complexType</xt> <xa>name</xa>=<xs>'java.util.Calendar'</xs><xt>></xt> |
| <xt><sequence></xt> |
| <xt><any</xt> <xa>processContents</xa>=<xs>'skip'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs> <xa>minOccurs</xa>=<xs>'0'</xs><xt>/></xt> |
| <xt></sequence></xt> |
| <xt></complexType></xt> |
| <xt><complexType</xt> <xa>name</xa>=<xs>'java.util.LinkedList_x003C_org.apache.juneau.examples.addressbook.Address_x003E_'</xs><xt>></xt> |
| <xt><sequence></xt> |
| <xt><choice</xt> <xa>minOccurs</xa>=<xs>'0'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs><xt>></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'address'</xs> <xa>_type</xa>=<xs>'addr:org.apache.juneau.examples.addressbook.Address'</xs><xt>/></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'null'</xs> <xa>_type</xa>=<xs>'string'</xs><xt>/></xt> |
| <xt></choice></xt> |
| <xt></sequence></xt> |
| <xt></complexType></xt> |
| <xt><complexType</xt> <xa>name</xa>=<xs>'boolean'</xs><xt>></xt> |
| <xt><simpleContent></xt> |
| <xt><extension</xt> <xa>base</xa>=<xs>'boolean'</xs><xt>/></xt> |
| <xt></simpleContent></xt> |
| <xt></complexType></xt> |
| <xt></schema></xt> |
| [\u0000] |
| <xt><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>></xt> |
| <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/2013/Juneau'</xs> <xa>schemaLocation</xa>=<xs>'juneau.xsd'</xs><xt>/></xt> |
| <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/address/'</xs> <xa>schemaLocation</xa>=<xs>'addr.xsd'</xs><xt>/></xt> |
| <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/mail/'</xs> <xa>schemaLocation</xa>=<xs>'mail.xsd'</xs><xt>/></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'person'</xs> <xa>_type</xa>=<xs>'per:org.apache.juneau.examples.addressbook.Person'</xs><xt>/></xt> |
| <xt><complexType</xt> <xa>name</xa>=<xs>'org.apache.juneau.examples.addressbook.Person'</xs><xt>></xt> |
| <xt><sequence></xt> |
| <xt><any</xt> <xa>minOccurs</xa>=<xs>'0'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs><xt>/></xt> |
| <xt></sequence></xt> |
| <xt><attribute</xt> <xa>name</xa>=<xs>'uri'</xs> <xa>_type</xa>=<xs>'string'</xs><xt>/></xt> |
| <xt></complexType></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'name'</xs> <xa>_type</xa>=<xs>'juneau:java.lang.String'</xs><xt>/></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'birthDate'</xs> <xa>_type</xa>=<xs>'juneau:java.util.Calendar'</xs><xt>/></xt> |
| <xt><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>/></xt> |
| <xt></schema></xt> |
| [\u0000] |
| <xt><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>></xt> |
| <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/2013/Juneau'</xs> <xa>schemaLocation</xa>=<xs>'juneau.xsd'</xs><xt>/></xt> |
| <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/person/'</xs> <xa>schemaLocation</xa>=<xs>'per.xsd'</xs><xt>/></xt> |
| <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/mail/'</xs> <xa>schemaLocation</xa>=<xs>'mail.xsd'</xs><xt>/></xt> |
| <xt><complexType</xt> <xa>name</xa>=<xs>'org.apache.juneau.examples.addressbook.Address'</xs><xt>></xt> |
| <xt><sequence></xt> |
| <xt><any</xt> <xa>minOccurs</xa>=<xs>'0'</xs> <xa>maxOccurs</xa>=<xs>'unbounded'</xs><xt>/></xt> |
| <xt></sequence></xt> |
| <xt></complexType></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'isCurrent'</xs> <xa>_type</xa>=<xs>'juneau:boolean'</xs><xt>/></xt> |
| <xt></schema></xt> |
| [\u0000] |
| <xt><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>></xt> |
| <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/2013/Juneau'</xs> <xa>schemaLocation</xa>=<xs>'juneau.xsd'</xs><xt>/></xt> |
| <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/person/'</xs> <xa>schemaLocation</xa>=<xs>'per.xsd'</xs><xt>/></xt> |
| <xt><import</xt> <xa>namespace</xa>=<xs>'http://www.apache.org/address/'</xs> <xa>schemaLocation</xa>=<xs>'addr.xsd'</xs><xt>/></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'street'</xs> <xa>_type</xa>=<xs>'juneau:java.lang.String'</xs><xt>/></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'city'</xs> <xa>_type</xa>=<xs>'juneau:java.lang.String'</xs><xt>/></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'state'</xs> <xa>_type</xa>=<xs>'juneau:java.lang.String'</xs><xt>/></xt> |
| <xt><element</xt> <xa>name</xa>=<xs>'zip'</xs> <xa>_type</xa>=<xs>'juneau:int'</xs><xt>/></xt> |
| <xt></schema></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> |