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 namespace attribute.
  • All JSON schema definitions are translated into XSD complexTypes as 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-xml element and sets the attribute property on the element to true.
  • Properties under an element are unordered in the XML. The properties receive an xs:all in the XSD rather than an xs: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-xml element 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:any if 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>