XML output and XSD translation
XML output
The JsonObject and TransformResult classes both support
serialization to XML rather than serialization to JSON. In both cases, exactly the same
serialization and validation rules apply to serializing out to XML as to JSON. Only the
output format differs.
- To serialize a JsonObject to XML, invoke one of the toXmlElement methods and then serialize the XmlElement as desired.
- To serialize a TransformResult to XML, invoke one of the toXmlElement methods. Then, serialize the XmlElement as desired. In the alternative, call one of the toXmlString convenience methods.
In addition, suppose that a REST API handler class returns a
TransformResult or JsonObject object. In this case,
the REST API framework automatically serializes the data to XML if the negotiated content
type is application/xml or text/xml.
XSD translation
The XML that is output from serializing a
JsonObject object with a schema, or
TransformResult object (which always has a schema), conforms to the XSD
that is produced as a transformation on the JSON Schema. In general, the translation to an
XSD follows the following rules:- The generated XSD has a single namespace of
http://guidewire.com/xsd/<schema-fqn> by default. You can
override this default by specifying the x-gw-xml property on the root
of the JSON schema and setting the
namespaceattribute. - All JSON schema definitions are translated into XSD
complexTypesas well as top-level elements that reference those types. That is, any such element is a legal document root. - By default, definition properties are mapped to sub-elements rather than attributes.
That is unless the schema property specifies the
x-gw-xmlelement and sets the attribute property on the element totrue. - Properties under an element are unordered in the XML. The properties receive an
xs:allin the XSD rather than anxs:sequence. - The element or attribute name for a property name has the same name in an XSD as a JSON schema property. The REST framework adjustes the name if it is not a legal XML element or attribute name.
- The frame work always wraps arrays in a container element with individual elements of
the array wrapped in elements. If the item elements are objects, they default to having
a name based on the referenced object definition. Alternatively, if the item elements
are scalars, they have the same name as the containing property. It is possible to
specify the
x-gw-xmlelement on the items and give a name property to explicitly name the child elements. - The framework maps JSON data types to equivalent XSD data types when available.
- The framework reproduces validation constraints such as minLength or maxLength on the property in the XSD whenever possible.
- The framework maps a schema definition that contains
additionalProperties into an XSD if no explicitly-named properties
are defined on the definition. However, the schema definition is represented as an
xs:anyif additionalProperties is mixed with explicitly-named properties.
Generating the XSDs
XSDs for JSON schema documents are produced using the standard tool that produces externalized schemas. That is the gwb genExternalSchemas task.
Example
Consider the following JSON schema
document:
{
"$schema": "http://json-schema.org/draft-04/schema#",
"definitions": {
"Contact": {
"type": "object",
"properties": {
"AllAddresses": {
"type": "array",
"items": {
"type": "object",
"$ref": "#/definitions/Address"
}
},
"EmailAddress1": {
"type": "string"
},
"FirstName": {
"type": "string"
},
"HomePhone": {
"type": "string"
},
"HomePhoneCountry": {
"type": "string",
"x-gw-type": "typekey.PhoneCountryCode"
},
"IntegerExt": {
"type": "integer",
"format": "int32"
},
"LastName": {
"type": "string"
},
"OfficialIDs": {
"type": "array",
"items": {
"$ref": "#/definitions/OfficialID"
}
},
"PrimaryAddress": {
"$ref": "#/definitions/Address"
},
"TagTypes": {
"type": "array",
"items": {
"type": "string",
"x-gw-type": "typekey.ContactTagType"
}
}
}
},
"OfficialID": {
"type": "object",
"properties": {
"Jurisdiction": {
"type": "string",
"x-gw-type": "typekey.Jurisdiction"
},
"Type": {
"type": "string",
"x-gw-type": "typekey.OfficialIDType"
},
"Value": {
"type": "string"
}
}
},
"TagMap": {
"type": "object",
"additionalProperties": {
"type": "string",
"x-gw-type": "typekey.ContactTagType",
"x-gw-export-enumeration": true
}
},
"Address": {
"type": "object",
"properties": {
"AddressLine1": {
"type": "string"
},
"AddressLine2": {
"type": "string"
},
"City": {
"type": "string"
}
}
}
},
"properties": {
"Contact": {
"type": "object",
"$ref": "#/definitions/Contact"
},
"OfficialID": {
"type": "object",
"$ref": "#/definitions/OfficialID"
},
"TagMap": {
"type": "object",
"$ref": "#/definitions/TagMap"
},
"Address": {
"type": "object",
"$ref": "#/definitions/Address"
}
}
}That produces the following
XSD:
<?xmlversion="1.0"?>
<xs:schema
targetNamespace="http://guidewire.com/xsd/gw.px.ete.contact-1.0"
version="1.0"
elementFormDefault="qualified"
xmlns="http://guidewire.com/xsd/gw.px.ete.contact-1.0"
xmlns:gw="http://guidewire.com/xsd"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:complexType
name="Contact">
<xs:all>
<xs:element
name="AllAddresses"
minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element
name="Address"
type="Address"
minOccurs="0"
maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element
name="EmailAddress1"
type="xs:string"
minOccurs="0"/>
<xs:element
name="FirstName"
type="xs:string"
minOccurs="0"/>
<xs:element
name="HomePhone"
type="xs:string"
minOccurs="0"/>
<xs:element
name="HomePhoneCountry"
type="xs:string"
minOccurs="0"
gw:type="typekey.PhoneCountryCode"/>
<xs:element
name="IntegerExt"
type="xs:int"
minOccurs="0"/>
<xs:element
name="LastName"
type="xs:string"
minOccurs="0"/>
<xs:element
name="OfficialIDs"
minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element
name="OfficialID"
type="OfficialID"
minOccurs="0"
maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element
name="PrimaryAddress"
type="Address"
minOccurs="0"/>
<xs:element
name="TagTypes"
minOccurs="0">
<xs:complexType>
<xs:sequence>
<xs:element
name="TagTypes"
type="xs:string"
minOccurs="0"
maxOccurs="unbounded"
gw:type="typekey.ContactTagType"/>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:all>
</xs:complexType>
<xs:complexType
name="OfficialID">
<xs:all>
<xs:element
name="Jurisdiction"
type="xs:string"
minOccurs="0"
gw:type="typekey.Jurisdiction"/>
<xs:element
name="Type"
type="xs:string"
minOccurs="0"
gw:type="typekey.OfficialIDType"/>
<xs:element
name="Value"
type="xs:string"
minOccurs="0"/>
</xs:all>
</xs:complexType>
<xs:complexType
name="TagMap">
<xs:sequence>
<xs:any
minOccurs="0"
maxOccurs="unbounded"
processContents="skip"/>
</xs:sequence>
</xs:complexType>
<xs:complexType
name="Address">
<xs:all>
<xs:element
name="AddressLine1"
type="xs:string"
minOccurs="0"/>
<xs:element
name="AddressLine2"
type="xs:string"
minOccurs="0"/>
<xs:element
name="City"
type="xs:string"
minOccurs="0"/>
</xs:all>
</xs:complexType>
<xs:element
name="Contact"
type="Contact"/>
<xs:element
name="OfficialID"
type="OfficialID"/>
<xs:element
name="TagMap"
type="TagMap"/>
</xs:schema>The following is the corresponding JSON
output:
{
"AllAddresses": [
{
"AddressLine1": "100 Main St."
}
],
"FirstName": "Alice",
"LastName": "Applegate"
}The JSON output would like the following when serialized to
XML:
<Contact xmlns="http://guidewire.com/xsd/gw.px.ete.contact-1.0">
<AllAddresses>
<Address>
<AddressLine1>100 Main St.</AddressLine1>
</Address>
</AllAddresses>
<FirstName>Alice</FirstName>
<LastName>Applegate</LastName>
</Contact>