Table of Contents
This section provides guidelines for designing an XML schema exported by a Java web service designed starting from Java. JAXB 2.0 provides a rich set of annotations and types for mapping Java classes to different XML Schema constructs. The guidelines provide guidance on using JAXB 2.0 annotations and types so that developer friendly bindings may be generated by XML serialization mechanisms (svcutil) on WCF client.
Not all JAXB 2.0 annotations are included here; not all are
relevant from an interoperability standpoint. For example, the
annotation @XmlAccessorType
provides control over default
serialization of fields and properties in a Java class but otherwise
has no effect on the on-the-wire XML representation or the XML schema
generated from a Java class. Select JAXB 2.0 annotations are therefore
not included here in the guidance.
The guidance includes several examples, which use the following conventions:
The prefix xs:
is used to represent XML
Schema namespace.
JAXB 2.0 annotations are defined in
javax.xml.bind.annotation
package but, for
brevity, the package name has been omitted.
This section covers the following topics:
Guideline: Java primitive and wrapper classes map to slightly different XML schema representations. Therefore, .NET bindings will vary accordingly.
Example 16.1. A Java primitive type and its corresponding wrapper class
//-- Java code fragment public class StockItem{ public Double wholeSalePrice; public double retailPrice; } //--Schema fragment <xs:complexType name="stockItem"> <xs:sequence> <xs:element name="wholeSalePrice" type="xs:double" minOccurs="0"/> <xs:element name="retailPrice" type="xs:double"/> </xs:sequence> </xs:complexType> //-- .NET C# auto generated code from schema public partial class stockItem { private double wholeSalePrice; private bool wholeSalePriceFieldSpecified; private double retailPrice; public double wholeSalePrice { get{ return this.wholeSalePrice;} set{this.wholeSalePrice=value} } public bool wholeSalePriceSpecified { get{ return this.wholeSalePriceFieldSpecified;} set{this.wholeSalePriceFieldSpecified=value} } public double retailPrice { get{ return this.retailPrice;} set{this.retailPrice=value} } } //-- C# code fragment stockItem s = new stockItem(); s.wholeSalePrice = Double.parse("198.92"); s.wholeSalePriceSpecified = true; s.retailPrice = Double.parse("300.25");
Guideline: Limit decimal values to
the range and precision of .NET's
System.decimal
.
java.math.BigDecimal
maps to
xs:decimal
. .NET maps xs:decimal
to
System.decimal
. These two data types support
different range and precision.
java.math.BigDecimal
supports arbitrary
precision. System.decimal
does not. For
interoperability use only values within the range and
precision of System.decimal
. (See
System.decimal.Minvalue
and
System.decimal.Maxvalue
.) Any values outside of
this range require a customized .NET client.
Example 16.2. BigDecimal
usage
//--- Java code fragment public class RetBigDecimal { private BigDecimal arg0; public BigDecimal getArg0() { return this.arg0; } public void setArg0(BigDecimal arg0) { this.arg0 = arg0; } } //--- Schema fragment <xs:complexType name="retBigDecimal"> <xs:sequence> <xs:element name="arg0" type="xs:decimal" minOccurs="0"/> </xs:sequence> </xs:complexType> //--- .NET auto generated code from schema public partial class retBigDecimal{ private decimal arg0Field; private bool arg0FieldSpecified; public decimal arg0 { get { return this.arg0Field;} set { this.arg0Field = value;} } public bool arg0Specified { get { return this.arg0FieldSpecified;} set { this.arg0FieldSpecified = value;} } } //--- C# code fragment System.CultureInfo engCulture = new System.CultureInfo("en-US"); retBigDecimal bd = new retBigDecimal(); bd.arg0 = System.decimal.MinValue; retBigDecimal negBd = new retBigDecimal(); negBd = System.decimal.Parse("-0.0", engCulture);
Guideline: Use the
@XmlSchemaType
annotation for a strongly typed
binding to a .NET client generated with the
DataContractSerializer
.
java.net.URI
maps to
xs:string
. .NET maps xs:string
to
System.string
. Annotation
@XmlSchemaType
can be used to define a more
strongly typed binding to a .NET client generated with the
DataContractSerializer
.
@XmlSchemaType
can be used to map
java.net.URI
to xs:anyURI
. .NET's
DataContractSerializer
and
XmlSerializer
bind xs:anyURI
differently:
DataContractSerializer
binds
xs:anyURI
to .NET type
System.Uri
.
XmlSerializer
binds
xs:anyURI
to .NET type
System.string
.
Thus, the above technique only works if the WSDL is
processed using DataContractSerializer
.
Example 16.3. @XmlSchemaType
and
DataContractSerializer
// Java code fragment public class PurchaseOrder { @XmlSchemaType(name="anyURI") public java.net.URI uri; } //-- Schema fragment <xs:complexType name="purchaseOrder"> <xs:sequence> <xs:element name="uri" type="xs:anyURI" minOccurs="0"/> </xs:sequence> </xs:complexType> //--- .NET auto generated code from schema //--- Using svcutil.exe /serializer:DataContractSerializer <wsdl file> public partial class purchaseOrder : object, System.Runtime.Serialization.IExtensibleDataObject { private System.Uri uriField; //-- ..... other generated code ........ public System.Uri uri { get { return this.uriField; } set { this.uriField = value; } } } //--- C# code fragment purchaseOrder tmpU = new purchaseOrder() tmpU.uri = new System.Uri("../Hello", System.UriKind.Relative);
Example 16.4. @XmlSchemaType
and
XmlSerializer
// Java code fragment public class PurchaseOrder { @XmlSchemaType(name="anyURI") public java.net.URI uri; } //--- .NET auto generated code from schema //--- Using svcutil.exe /serializer:XmlSerializer <wsdl file> public partial class purchaseOrder { private string uriField; public string uri { get { return this.uriField; } set { this.uriField = value; } } } //--- C# code fragment purchaseOrder tmpU = new purchaseOrder() tmpU.uri = "mailto:mailto:mduerst@ifi.unizh.ch";
Guideline: Use .NET's
System.Xml.XmlConvert
to generate a lexical
representation of xs:duration
when the binding is
to a type of System.string
.
javax.xml.datatype.Duration
maps to
xs:duration
. .NET maps xs:duration
to a different datatype for
DataContractSerializer
and
XmlSerializer
.
DataContractSerializer
binds
xs:duration
to .NET
System.TimeSpan
.
XmlSerializer
binds
xs:duration
to .NET
System.string
.
When xs:duration
is bound to .NET
System.string
, the string value must be a lexical
representation for xs:duration
. .NET provides
utility System.Xml.XmlConvert
for this
purpose.
Example 16.5. Mapping xs:duration
using
DataContactSerializer
//-- Java code fragment public class PurchaseReport { public javax.xml.datatype.Duration period; } //-- Schema fragment <xs:complexType name="purchaseReport"> <xs:sequence> <xs:element name="period" type="xs:duration" minOccurs="0"/> </xs:sequence> </xs:complexType> //-- .NET auto generated code from schema //-- Using svcutil.exe /serializer:DataContractSerializer <wsdl file> public partial class purchaseReport: object, System.Runtime.Serialization.IExtensibleDataObject { private System.TimeSpan periodField; //-- ..... other generated code ........ public System.TimeSpan period { get { return this.periodField; } set { this.periodField = value; } } } //-- C# code fragment purchaseReport tmpR = new purchaseReport(); tmpR.period = new System.TimeSpan.MaxValue;
Example 16.6. Mapping xs:duration
using
XmlSerializer
//-- .NET auto generated code from schema //-- Using svcutil.exe /serializer:XmlSerializer <wsdl file> public partial class purchaseReport { private string periodField; public string period { get { return this.periodField; } set { this.periodField = value; } } } //-- C# code fragment purchaseReport tmpR = new purchaseReport(); tmpR.period = System.Xml.XmlConvert.ToString(new System.TimeSpan(23, 0,0));
Guideline:
java.awt.Image
,
javax.xml.transform.Source
, and
javax.activation.DataHandler
map to
xs:base64Binary
. .NET maps
xs:base64Binary
to byte[]
.
JAXB 2.0 provides the annotation
@XmlMimeType
, which supports specifying the
content type, but .NET ignores this information.
Example 16.7. Mapping java.awt.Image
without
@XmlMimeType
//-- Java code fragment public class Claim { public java.awt.Image photo; } //-- Schema fragment <xs:complexType name="claim"> <xs:sequence> <xs:element name="photo" type="xs:base64Binary" minOccurs="0"/> </xs:sequence> </xs:complexType> //-- .NET auto generated code from schema public partial class claim : object, System.Runtime.Serialization.IExtensibleDataObject { private byte[] photoField; //-- ..... other generated code ....... public byte[] photo { get { return this.photoField; } set { this.photoField = value; } } } //-- C# code fragment try { claim tmpC = new claim(); System.IO.FileStream f = new System.IO.FileStream( "C:\\icons\\circleIcon.gif", System.IO.FileMode.Open); int cnt = (int)f.Length; tmpC.photo = new byte[cnt]; int rCnt = f.Read(tmpC.photo, 0, cnt); } catch (Exception e) { Console.WriteLine(e.ToString()); }
Example 16.8. Mapping java.awt.Image
with
@XmlMimeType
//-- Java code fragment public class Claim { @XmlMimeType("image/gif") public java.awt.Image photo; } //-- Schema fragment <xs:complexType name="claim"> <xs:sequence> <xs:element name="photo" ns1:expectedContentTypes="image/gif" type="xs:base64Binary" minOccurs="0" xmlns:ns1="http://www.w3.org/2005/05/xmlmime"/> </xs:sequence> </xs:complexType> //-- Using the @XmlMimeType annotation doesn't change .NET //--auto generated code public partial class claim : object, System.Runtime.Serialization.IExtensibleDataObject { private byte[] photoField; //-- ..... other generated code ....... public byte[] photo { get { return this.photoField; } set { this.photoField = value; } } } //-- This code is unchanged by the different schema //-- C# code fragment try { claim tmpC = new claim(); System.IO.FileStream f = new System.IO.FileStream( "C:\\icons\\circleIcon.gif", System.IO.FileMode.Open); int cnt = (int)f.Length; tmpC.photo = new byte[cnt]; int rCnt = f.Read(tmpC.photo, 0, cnt); } catch (Exception e) { Console.WriteLine(e.ToString()); }
Guideline: Use
java.xml.datatype.XMLGregorianCalendar
instead of
java.util.Date
and
java.util.Calendar
.
XMLGregorianCalendar
supports the following
XML schema calendar types: xs:date
,
xs:time
, xs:dateTime
,
xs:gYearMonth
, xs:gMonthDay
,
xs:gYear
, xs:gMonth
, and
xs:gDay
. It is statically mapped to
xs:anySimpleType
, the common schema type from
which all the XML schema calendar types are dervived. .NET
maps xs:anySimpleType
to
System.string
.
java.util.Date
and
java.util.Calendar
map to
xs:dateTime
, but don’t provide as complete
XML support as XMLGregorianCalendar
does.
Guideline: Use the annotation
@XmlSchemaType
for a strongly typed binding of
XMLGregorianCalendar
to one of the XML schema
calendar types.
Example 16.9. XmlGregorianCalendar
without
@XmlSchemaType
//-- Java code fragment public class PurchaseOrder { public javax.xml.datatype.XMLGregorianCalendar orderDate; } //-- Schema fragment <xs:complexType name="purchaseOrder"> <xs:sequence> <xs:element name="orderDate" type="xs:anySimpleType" minOccurs="0"/> </xs:sequence> </xs:complexType> //-- .NET auto generated code from schema public partial class purchaseOrder { private string orderDateField; public string orderDate { get { return this.orderDateField; } set { this.orderDateField = value; } } } //-- C# code fragment purchaseOrder tmpP = new purchaseOrder(); tmpP.orderDate = System.Xml.XmlConvert.ToString( System.DateTime.Now, System.Xml.XmlDateTimeSerializerMode.RoundtripKind);
Example 16.10. XMLGregorianCalendar
with
@XmlSchemaType
//-- Java code fragment public class PurchaseOrder { @XmlSchemaType(name="dateTime") public javax.xml.datatype.XMLGregorianCalendar orderDate; } //-- Schema fragment <xs:complexType name="purchaseOrder"> <xs:sequence> <xs:element name="orderDate" type="xs:dateTime" minOccurs="0"/> </xs:sequence> </xs:complexType> //-- .NET auto generated code from schema public partial class purchaseOrder : object, System.Runtime.Serialization.IExtensibleDataObject { private System.Runtime.Serialization.ExtensionDataObject extensionDataField; private System.DateTime orderDateField; public System.Runtime.Serialization.ExtensionDataObject ExtensionData { get { return this.extensionDataField; } set { this.extensionDataField = value; } } public System.DateTime orderDate { get { return this.orderDateField; } set { this.orderDateField = value; } } } //-- C# code fragment purchaseOrder tmpP = new purchaseOrder(); tmpP.orderDate = System.DateTime.Now;
Guideline: Use Leach-Salz variant of UUID at runtime.
java.util.UUID
maps to schema type
xs:string
. .NET maps xs:string
to
System.string
. The constructors in
java.util.UUID
allow any variant of UUID to be
created. Its methods are for manipulation of the Leach-Salz
variant.
Example 16.11. Mapping UUID
//-- Java code fragment public class ReportUid { public java.util.UUID uuid; } //-- Schema fragment <xs:complexType name="reportUid"> <xs:sequence> <xs:element name="uuid" type="xs:string" minOccurs="0"/> </xs:sequence> </xs:complexType> //-- .NET auto generated code from schema public partial class reportUid: object, System.Runtime.Serialization.IExtensibleDataObject { private System.Runtime.Serialization.ExtensionDataObject extensionDataField; private string uuidField; public System.Runtime.Serialization.ExtensionDataObject ExtensionData { get { return this.extensionDataField; } set { this.extensionDataField = value; } } public string uuid { get { return this.uuidField; } set { this.uuidField = value; } } } //-- C# code fragment reportUid tmpU = new reportUid(); System.Guid guid = new System.Guid("06b7857a-05d8-4c14-b7fa-822e2aa6053f"); tmpU.uuid = guid.ToString();
Guideline: A typed variable maps to
xs:anyType
. .NET maps xs:anyType
to
System.Object
.
Example 16.12. Using a typed variable
// Java class public class Shape <T> { private T xshape; public Shape() {}; public Shape(T f) { xshape = f; } } //-- Schema fragment <xs:complexType name="shape"> <xs:sequence> <xs:element name="xshape" type="xs:anyType" minOccurs="0"/> </xs:sequence> </xs:complexType> // C# code generated by svcutil public partial class shape { private object xshapeField; public object xshape { get { return this.xshapeField; } set { this.xshapeField = value; } } }
Java collections types
(java.util.Collection
and its subtypes, array,
List, and parameterized collection types such as
List<Integer>
) can be mapped to XML schema
in different ways and can be serialized in different ways. The
following examples show .NET bindings.
Guideline: By default, a
collection type such as List<Integer>
maps to an XML schema construct that is a repeating
unbounded occurrence of an optional and nillable element.
.NET binds the XML schema construct to
System.Nullable<int>[]
. The element is
optional and nillable. However, when marshalling JAXB
marshaller will always marshal a null value using
xsi:nil
.
Example 16.13. Collection to a list of nillable elements
//-- Java code fragment @XmlRootElement(name="po") public PurchaseOrder { public List<Integer> items; } //-- Schema fragment <xs:element name="po" type="purchaseOrder"> <xs:complexType name="purchaseOrder"> <xs:sequence> <xs:element name="items" type="xs:int" nillable="true" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> ... //--- JAXB XML serialization <po> <items> 1 </items> <items> 2 </items> <items> 3 </items> </po> <po> <items> 1 </items> <items xsi:nil=true/> <items> 3 </items> </po> //-- .NET auto generated code from schema partial class purchaseOrder { private System.Nullable<int>[] itemsField; public System.Nullable<int>[] items { get { return this.itemsField; } set { this.itemsField = value; } } }
Guideline: This is the same as
above except that a collection type such as
List<Integer>
maps to a repeating
unbounded occurrence of an optional
(minOccurs="0"
) but not nillable element.
This in turn binds to .NET type int[]
. This
is more developer friendly. However, when marshalling,
JAXB will marshal a null value within the
List<Integer>
as a value that is absent
from the XML instance.
Example 16.14. Collection to a list of optional elements
//-- Java code fragment @XmlRootElement(name="po") public PurchaseOrder { @XmlElement(nillable=false) public List<Integer> items; } //-- Schema fragment <xs:element name="po" type="purchaseOrder"> <xs:complexType name="purchaseOrder"> <xs:sequence> <xs:element name="items" type="xs:int" minOccurs="0" maxOccurs="unbounded"/> </xs:sequence> </xs:complexType> ... // .NET auto generated code from schema partial class purchaseOrder { private int[] itemsField; public int[] items { get { return this.itemsField; } set { this.itemsField = value; } } }
Guideline: A collection such as
List<Integer>
can be mapped to a list
of XML values (that is, an XML schema list simple type)
using annotation @XmlList
. .NET maps a list
simple type to a .NET System.string
.
Example 16.15. Collection to a list of values using
@XmlList
//-- Java code fragment @XmlRootElement(name="po") public PurchaseOrder { @XmlList public List<Integer> items; } //-- Schema fragment <xs:element name="po" type="purchaseOrder"> <xs:complexType name="purchaseOrder"> <xs:element name="items" minOccurs="0"> <xs:simpleType> <xs:list itemType="xs:int"/> </xs:simpleType> </xs:element> </xs:complexType> ... //-- XML serialization <po> <items> 1 2 3 </items> </po> // .NET auto generated code from schema partial class purchaseOrder { private string itemsField; public string items { get { return this.itemsField; } set { this.itemsField = value; } } }
Example 16.16. Single and multidimensional arrays
//-- Java code fragment public class FamilyTree { public Person[] persons; public Person[][] family; } // .NET auto generated code from schema public partial class familyTree { private person[] persons; private person[][] families; public person[] persons { get { return this.membersField; } set { this.membersField = value; } } public person[][] families { get { return this.familiesField; } set { this.familiesField = value; } } }
The following guidelines apply to mapping of JavaBeans properties and Java fields, but for brevity Java fields are used.
Guideline: The
@XmlElement
annotation maps a property or field
to an XML element. This is also the default mapping in the
absence of any other JAXB 2.0 annotations. The annotation
parameters in @XmlElement
can be used to specify
whether the element is optional or required, nillable or not.
The following examples illustrate the corresponding bindings
in the .NET client.
Example 16.17. Map a field or property to a nillable element
//-- Java code fragment public class PurchaseOrder { // Map a field to a nillable XML element @javax.xml.bind.annotation.XmlElement(nillable=true) public java.math.BigDecimal price; } //-- Schema fragment <xs:complexType name="purchaseOrder"> <xs:sequence> <xs:element name="price" type="xs:decimal" nillable="true" minOccurs="0" /> </xs:sequence> </xs:complexType> // .NET auto generated code from schema public partial class purchaseOrder { private System.Nullable<decimal> priceField; private bool priceFieldSpecified; public decimal price { get { return this.priceField; } set { this.priceField = value; } } public bool priceSpecified { { get { return this.priceFieldSpecified; } set { this.priceFieldSpecified = value;} } }
Example 16.18. Map a property or field to a nillable, required element
//-- Java code fragment public class PurchaseOrder { // Map a field to a nillable XML element @XmlElement(nillable=true, required=true) public java.math.BigDecimal price; } //-- Schema fragment <xs:complexType name="purchaseOrder"> <xs:sequence> <xs:element name="price" type="xs:decimal" nillable="true" minOccurs="1" /> </xs:sequence> </xs:complexType> // .NET auto generated code from schema public partial class purchaseOrder { private System.Nullable<decimal> priceField; public decimal price { get { return this.priceField; } set { this.priceField = value; } } }
Guideline: A property or field can
be mapped to an XML attribute using @XmlAttribute
annotation. .NET binds an XML attribute to a property.
Example 16.19. Mapping a field or property to an XML attribute
//-- Java code fragment public class UKAddress extends Address { @XmlAttribute public int exportCode; } //-- Schema fragment <! XML Schema fragment --> <xs:complexType name="ukAddress"> <xs:complexContent> <xs:extension base="tns:address"> <xs:sequence/> <xs:attribute name="exportCode" type="xs:int"/> </xs:extension> </xs:complexContent> </xs:complexType> // .NET auto generated code from schema public partial class ukAddress : address { private int exportCodeField; public int exportCode { get { return this.exportCodeField; } set { this.exportCodeField = value; } } }
Guideline:
@XmlElementRefs
maps to a xs:choice
.
This binds to a property with name item
in the C#
class. If there is another field/property named
item
in the Java class, there will be a name
clash that .NET will resolve by generating name. To avoid the
name clash, either change the name or use customization, for
example @XmlElement(name="foo")
.
Example 16.20. Mapping a field or property using
@XmlElementRefs
//-- Java code fragment public class PurchaseOrder { @XmlElementRefs({ @XmlElementRef(name="plane", type=PlaneType.class), @XmlElementRef(name="auto", type=AutoType.class)}) public TransportType shipBy; } @XmlRootElement(name="plane") public class PlaneType extends TransportType {} @XmlRootElement(name="auto") public class AutoType extends TransportType { } @XmlRootElement public class TransportType { ... } //-- Schema fragment <!-- XML schema generated by wsgen --> <xs:complexType name="purchaseOrder"> <xs:choice> <xs:element ref="plane"/> <xs:element ref="auto"/> </xs:choice> </xs:complexType> <!-- XML global elements --> <xs:element name="plane" type="autoType" /> <xs:element name="auto" type="planeType" /> <xs:complexType name="autoType"> <!-- content omitted - details not relevant to example --> </xs:complexType> </xs:complexType name="planeType"> <!-- content omitted - details not relevant to example --> </xs:complexType> // .NET auto generated code from schema public partial class purchaseOrder { private transportType itemField; [System.Xml.Serialization.XmlElementAttribute("auto", typeof(autoType), Order=4)] [System.Xml.Serialization.XmlElementAttribute("plane", typeof(planeType), Order=4)] public transportType Item { get { return this.itemField; } set { this.itemField = value; } } public partial class planeType { ... } ; public partial class autoType { ... } ;
A Java class can be mapped to different XML schema type and/or an XML element. The following guidelines apply to the usage of annotations at the class level.
Guideline: Prefer mapping class to named XML schema type rather than an anonymous type for a better .NET type binding.
The @XmlType
annotation is used to
customize the mapping of a Java class to an anonymous type.
.NET binds an anonymous type to a .NET class - one per
reference to the anonymous type. Thus, each Java class mapped
to an anonymous type can generate multiple classes on the .NET
client.
Example 16.21. Mapping a Java class to an anonymous type using
@XmlType
//-- Java code fragment public class PurchaseOrder { public java.util.List<Item> item; } @XmlType(name="") public class Item { public String productName; ... } //-- Schema fragment <xs:complexType name="purchaseOrder"> <xs:sequence> <xs:element name="item"> <xs:complexType> <xs:sequence> <xs:element name="productName" type="xs:string"/> </xs:sequence> </xs:complexType > </xs:element> </xs:sequence> </xs:complexType> // C# code generated by svcutil public partial class purchaseOrder { private purchaseOrderItem[] itemField; System.Xml.Serialization.XmlElementAttribute("item", Form=System.Xml.Schema.XmlSchemaForm.Unqualified, IsNullable=true, Order=0)] public purchaseOrderItem[] item { get { return this.itemField; } set { this.itemField = value; } } } // .NET auto generated code from schema public partial class purchaseOrderItem { private string productNameField; public string productName { get { return this.productNameField; } set { this.productNameField = value; } } }
Guideline: Avoid using
XmlType(propOrder=:{})
.
@XmlType(propOrder={})
maps a Java class to
an XML Schema complex type with xs:all
content
model. Since XML Schema places severe restrictions on
xs:all
, the use of
@XmlType(propOrder={})
is therefore not
recommended. So, the following example shows the mapping of a
Java class to xs:all
, but the corresponding .NET
code generated by svcutil
is omitted.
Example 16.22. Mapping a class to xs:all
using
@XmlType
//-- Java code fragment @XmlType(propOrder={}) public class USAddress { public String name; public String street; } //-- Schema fragment <xs:complexType name="USAddress"> <xs:all> <xs:element name="name" type="xs:string"/> <xs:element name="street" type="xs:string"/> ... </xs:all> </xs:complexType>
Guideline: A class can be mapped to
a complexType
with a simpleContent
using @XmlValue
annotation. .NET binds the Java
property annotated with @XmlValue
to a property
with name "value"
.
Example 16.23. Class to complexType
with
simpleContent
//-- Java code fragment public class InternationalPrice { @XmlValue public java.math.BigDecimal price; @XmlAttribute public String currency; } //-- Schema fragment <xs:complexType name="internationalPrice"> <xs:simpleContent> <xs:extension base="xs:decimal"> xs:attribute name="currency" type="xs:string"/> </xs:extension> </xs:simpleContent> </xs:complexType> // .NET auto generated code from schema public partial class internationalPrice { private string currencyField; private decimal valueField; public string currency { get { return this.currencyField; } set { this.currencyField = value;} } public decimal Value { get { return this.valueField; } set { this.valueField = value;} } }
JAXB 2.0 supports the following annotations for defining open content. (Open content allows content not statically defined in XML schema to occur in an XML instance):
The @XmlAnyElement
annotation maps to
xs:any
, which binds to the .NET type
System.Xml.XmlElement[]
.
The @XmlAnyAttribute
annotation maps to
xs:anyAttribute
, which binds to the .NET type
System.Xml.XmlAttribute[]
.
Example 16.24. Using @XmlAnyElement
for open
content
//-- Java code fragment @XmlType(propOrder={"name", "age", "oc"}) public class OcPerson { @XmlElement(required=true) public String name; public int age; // Define open content @XmlAnyElement public List<Object> oc; } //-- Schema fragment <xs:complexType name="ocPerson"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="age" type="xs:int"/> <xs:any minOccurs="0" maxOccurs="unbounded"> </xs:sequence> </xs:complexType> // .NET auto generated code from schema public class ocPerson { private String name; private int age; private System.Xml.XmlElement[] anyField;< public String name { ... } public int age { ... } public System.Xml.XmlElement[] Any { { get { return this.anyField; } set { this.anyField = value; } } }
Example 16.25. Using @XmlAnyAttribute
for open
content
//-- Java code fragment @XmlType(propOrder={"name", "age"} public class OcPerson { public String name; public int age; // Define open content @XmlAnyAttribute public java.util.Map oc; } //-- Schema fragment <xs:complexType name="ocPerson"> <xs:sequence> <xs:element name="name" type="xs:string"/> <xs:element name="age" type="xs:int"/> </xs:sequence> <xs:anyAttribute/> </xs:complexType> // .NET auto generated code from schema public class ocPerson { private String name; private double age; private System.Xml.XmlAttribute[] anyAttrField;< public String name { ... } public double age { ... } public System.Xml.XmlElement[] anyAttr { { get { return this.anyAttrField; } set { this.anyAttrField = value; } } }
Guideline: A Java enum
type maps to an XML schema type constrained by enumeration facets.
This, in turn, binds to the .NET type enum
type.
Example 16.26. Java enum
↔ xs:simpleType
(with enum
facets) ↔ .NET
enum
//-- Java code fragment public enum USState {MA, NH} //-- Schema fragment <xs:simpleType name="usState"> <xs:restriction base="xs:string"> <xs:enumeration value="NH" /> <xs:enumeration value="MA" /> </xs:restriction> </xs:simpleType> // .NET auto generated code from schema public enum usState { NH, MA }
The following package-level JAXB annotations are relevant from an interoperability standpoint:
@XmlSchema
- customizes the mapping of
package to XML namespace.
@XmlSchemaType
- customizes the mapping
of XML schema built-in type. The
@XmlSchemaType
annotation can also be used at
the property/field level, as was seen in the example XMLGregorianCalendar Type.
A package is mapped to an XML namespace. The following
attributes of the XML namespace can be customized using the
@XmlSchema
annotation parameters:
elementFormDefault
using
@XmlSchema.elementFormDefault()
attributeFormDefault
using
@XmlSchema.attributeFormDefault()
targetNamespace
using
@XmlSchema.namespace()
Associate namespace prefixes with the XML
namespaces using the @XmlSchema.ns()
annotation
These XML namespace attributes are bound to .NET
serialization attributes (for example,
XmlSerializer
attributes).
Any JAXB 2.0 annotation can be used, but the following are not recommended:
The
javax.xml.bind.annotation.XmlElementDecl
annotation is used to provide complete XML schema
support.
The @XmlID
and
@XmlIDREF
annotations are used for XML
object graph serialization, which is not well
supported.
The following guidelines apply when designing a Java web service starting from a WSDL:
If the WSDL was generated by
DataContractSerializer
, enable JAXB 2.0
customizations described in Customizations for WCF Service WSDL. The rationale for the JAXB 2.0
customizations is described in the same section.
If the WSDL is a result of contract first approach,
verify that the WSDL can be processed by either the
DataContractSerializer
or
XmlSerializer
mechanisms.
The purpose of this step is to ensure that the WSDL uses
only the set of XML schema features supported by JAXB 2.0 or
.NET serialization mechanisms. JAXB 2.0 was designed to
support all the XML schema features. The WCF serialization
mechanisms, DataContractSerializer
and
XmlSerializer
, provide different levels of
support for XML schema features. Thus, the following step will
ensure that the WSDL/schema file can be consumed by the WCF
serialization mechanisms.
svcutil wsdl-file
The svcutil.exe
tool, by default, uses
DataContractSerializer
but falls back to
XmlSerializer
if it encounters an XML schema
construct not supported by XmlFormatter
.
When developing either a Java web service or a Java client from
a WCF service WSDL generated using
DataContractSerializer
, the following JAXB 2.0
customizations are useful and/or required.
generateElementProperty
attribute
mapSimpleTypeDef
attribute
The following sections explain the use and rationale of these customizations.
WCF service WSDL generated from a programming language such
as C# using DataContractSerializer
may contain XML
Schema constructs which result in
JAXBElement<T>
in generated code. A
JAXBElement<T>
type can also sometimes be
generated when a WSDL contains advanced XML schema features such
as substitution groups or elements that are both optional and
nillable. In all such cases, JAXBElement<T>
provides roundtripping support of values in XML instances.
However, JAXBElement<T>
is not natural to a
Java developer. So the generateElementProperty
customization can be used to generate an alternate developer
friendly but lossy binding. The different bindings along with the
trade-offs are discussed below.
The following is the default binding of an optional
(minOccurs="0")
and
nillable(nillable="true")
element:
Example 16.27.
<!-- XML schema fragment --> <xs:element name="person" type="Person"/> <xs:complexType name="Person"> <xs:sequence> <xs:element name="name" type="xs:string" nillable="true" minOccurs="0"/> </xs:sequence> </xs:complexType> ...
Example 16.28.
// Binding public class Person { JAXBElement<String> getName() {...}; public void setName(JAXBElement<String> value) {...} }
Since the XML element name
is both optional
and nillable, it can be represented in an XML instance in one
of following ways:
Example 16.29.
<!-- Absence of element name--> <person> <-- element name is absent --> </person> <!-- Presence of an element name --> <person> <name xsi:nil="true"/> </person>
The JAXBElement<String>
type
roundtrips the XML representation of name
element
across an unmarshal/marshal operation.
When generateElementProperty
is false, the
binding is changed as follows:
Example 16.30.
// set JAXB customization generateElementProperty="false" public class Person { String getName() {...} public void setName(String value) {...} }
The above binding is more natural to Java developer than
JAXBElement<String>
. However, it does not
roundtrip the value of name
.
JAXB 2.0 allows generateElementProperty
to
be set:
Globally in
<jaxb:globalBindings>
Locally in <jaxb:property>
customization
When processing a WCF service WSDL, it is recommended
that the generateElementProperty
customization be
set in <jaxb:globalBindings>
:
Example 16.31.
<jaxb:bindings version="2.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <jaxb:bindings schemaLocation="schema-importedby-wcfsvcwsdl" node="/xs:schema"> <jaxb:globalBindings generateElementProperty="false"/> </jaxb:bindings> ...
The generateElementProperty
attribute
was introduced in JAXB 2.1.
XML Schema Part 2: Datatype defines facilities for defining datatypes for use in XML Schemas. .NET platform introduced the CLR types for some of the XML schema datatypes as described in CLR to XML Schema Type Mapping.
Table 16.1. CLR to XML Schema Type Mapping
CLR Type | XML Schema Type |
---|---|
| |
| |
| |
| |
However, there are no corresponding Java types that map to the XML Schema types listed in CLR to XML Schema Type Mapping. Furthermore, JAXB 2.0 maps these XML schema types to Java types that are natural to Java developer. However, this results in a mapping that is not one-to-one. For example:
xs:int -> int
xs:unsignedShort -> int
The lack of a one-to-one mapping means that when XML
Schema types shown in CLR to XML Schema Type Mapping are used in an
xsi:type
construct, they won’t be preserved
by default across an unmarshal followed by marshal operation.
For example:
Example 16.32.
// C# web method public Object retObject(Object objvalue); // Java web method generated from WCF service WSDL public Object retObject( Object objvalue); }
The following illustrates why xsi:type
is
not preserved across an unmarshal/marshal operation.
A value of type uint
is marshalled
by WCF serialization mechanism as:
JAXB 2.0 unmarshaller unmarshals the value as an
instance of int
and assigns it to
parameter objvalue
.
The objvalue
is marshalled back by
JAXB 2.0 marshaller with an xsi:type
of
xs:int
.
One way to preserve and roundtrip the
xsi:type
is to use the
mapSimpleTypeDef
customization. The customization
makes the mapping of XML Schema Part 2 datatypes one--to-one
by generating additional Java classes. Thus,
xs:unsignedShort
will be bound to its own class
rather than int
, as shown:
The following illustrates how the xsi:type
is preserved across an unmarshal/marshal operation:
A value of type uint
is marshalled
by WCF serialization mechanism as:
JAXB 2.0 unmarshaller unmarshals the value as an
instance of UnsignedShort
and assigns it
to parameter objvalue
.
The objvalue
is marshalled back by
JAXB 2.0 marshaller with an xsi:type
of
xs:int
.
Guideline: Use the
mapSimpleTypedef
customization where
roundtripping of XML Schema types in CLR to XML Schema Type Mapping are used in xsi:type
.
However, it is preferable to avoid the use of CLR types listed
in CLR to XML Schema Type Mapping since
they are specific to .NET platform.
The syntax of the mapSimpleTypeDef
customization is shown below.
Example 16.38.
<jaxb:bindings version="2.0" xmlns:jxb="http://java.sun.com/xml/ns/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <jaxb:bindings schemaLocation="schema-importedby-wcfsvcwsdl"> <jaxb:globalBindings mapSimpleTypeDef="true"/> </jaxb:bindings> ....
This section describes how to develop a .NET client that uses data binding.
To Develop a Microsoft .NET Client
Perform the following steps to generate a Microsoft .NET client from a Java web service WSDL file.
Generate WCF web service
client artifacts using the svcutil.exe
tool:
svcutil.exe java-web-service-wsdl
svcutil.exe
has the following options
for selecting a serializer:
svcutil.exe /serializer:auto
(default)
svcutil.exe
/serializer:DataContractSerializer
svcutil.exe
/serializer:XmlSerializer
It is recommended that you use the default option,
/serializer:auto
. This option ensures that
svcutil.exe
falls back to
XmlSerializer
if an XML schema construct is
used that cannot be processed by
DataContractSerializer
.
For example, in the following class the field
price
is mapped to an XML attribute that
cannot be consumed by
DataContractSerializer
.
Example 16.39.
public class POType { @javax.xml.bind.annotation.XmlAttribute public java.math.BigDecimal price; } <!-- XML schema fragment --> <xs:complexType name="poType"> <xs:sequence/> <xs:attribute name="price" type="xs:decimal"/> </xs:complexType>
Develop the .NET client using the generated artifacts.
JAX-WS 2.0 enforces strict Basic Profile 1.1 compliance. In one situation, the .NET framework does not enforce strict BP 1.1 semantics, and their usage can lead to interoperability problems.
In rpclit
mode, BP
1.1, R2211 disallows the use of xsi:nil
in part
accessors: An ENVELOPE described with an
rpc-literal
binding MUST NOT have
the xsi:nil
attribute with a value of "1"
or
"true"
on the part accessors.
From a developer perspective this means that in
rpclit
mode, JAX-WS does not allow a null to be passed in
a web service method parameter.
Example 16.40.
//Java Web method public byte[] retByteArray(byte[] inByteArray) { return inByteArray; }
Example 16.41.
<!-- In rpclit mode, the above Java web service method will throw an exception if the following XML instance with xsi:nil is passed by a .NET client.--> <RetByteArray xmlns="http://tempuri.org/"> <inByteArray a:nil="true" xmlns="" xmlns:a="http://www.w3.org/2001/XMLSchema-instance"/> </RetByteArray>