- Working Example
- Basic concepts
- Element declarations
- Simple types
- Built-in simple types
- Deriving simple types by list
- Deriving simple types by union
- Deriving simple types by restriction
- Defining custom simple types
- Complex types
- Basic property characteristics
- Defining properties
- Value property
- Attribute property
- Any attribute property
- Element property
- Elements property
- Element map property
- Element reference property
- Element references property
- Any element property
- Generating mappings from XML Schema
|Take a look at this question on Stackoverflow which incepted the development of Jsonix.|
These conversions are based on simple XML/JSON mappings which can be written manually or generated from an XML Schema.
In short, with Jsonix you can:
- parse XML into JSON;
- serialize JSON into XML;
- define mappings between XML and JSON declaratively;
- generate these mappings from XML Schemas.
Jsonix mappings are heavily influenced by JAXB annotations.
Jsonix schema compiler is based on XJC, schema compiler from the JAXB Reference Implementation.
- Supports string data, DOM nodes or URLs as source
- Supports string data and DOM nodes as result
- Provides extensible type system
- Supports most XML Schema simple types
- Supports enumerations, list and union simple types
- Allows adding own simple types
- Supports complex types consisting of several properties
- Supports deriving complex types by extensions
- Provides advanced property system
- Value, attribute, element, element reference properties for string processing of XML content
- Any attribute, any element properties for "lax" processing for XML content
This chapter demonstrates the usage of Jsonix in a classic "purchase order" example.
Here's an example of XML for this schema:
Here's how you would parse this XML document with Jsonix:
Now let us take a look at the XML/object mappings, the part we skipped previously:
There is, however, another possibility for creating Jsonix mappings: you can generate them automatically from an XML Schema. Jsonix provides a schema compiler which take an XML Schema as input and generates Jsonix mappings for it. So instead of writing PO.js per hand you can generate it from an XML Schema (po.xsd) using the Jsonix schema compiler:
Jsonix mappings are defined in a module object which provides information about declared types and XML elements which they are mapped to. Below is a very simple module One which declares a complex type One.ValueType (containing a single property value) and maps this type to the global XML element value:
Provided this module object, we can create a Jsonix context and use it for marshalling or unmarshalling:
Now we can enumerate basic components of Jsonix mappings:
These components will be described in the following sections
In the previous section we've seen a declaration of module One. Here's even simpler module:
This module maps the XML element string to the string type. With this module, we'd unmarshal the following XML:
Apart from this required elementInfos property, modules may also declare types - just like the module One declared the type One.ValueType.
There are no specific requirements for how exactly the module object should be built. Below is a recommended flow:
This structure is mainly driven by the fact that you can't use the type object before you declare it. That's why types are declared before properties (which may need to use these types).
Every valid XML document has a single root element which is called the document element. When unmarshalling an XML document, Jsonix runtime needs to know onto which type does the root element of this document map. For instance that the element value maps onto the type One.ValueType in the module One.
This mapping is defined by the elementInfos property of the module object. The elementInfos property is an array of element declarations. Each element declaration is an object with the following structure:
|Element declaration has two more options, scope and substitutionHead which will be explained later on.|
Element mapping defined above maps an element like:
onto the type MyModule.MyType declared in the MyModule module. So when Jsonix unmarshals such an element it will produce a result like:
You mostly need to declare only your global elements in MyModule.elementInfos. All other elements, attributes etc. mappings are done via properties.
|However, you may also need to use MyModule.elementInfos to declare scoped elements.|
A concept of type is a central concept in Jsonix mappings. Element declarations map XML elements onto types; most of the properties have a target type and so on.
Jsonix provides supports most simple types defined in the XML Schema out of the box. You can also define your own simple types using derivation by list, by union, defining enumerations or writing a custom simple type.
Jsonix supports most simple types defined in the XML Schema. These types are called built-in simple types and are based on the following hierarchy of types:
|Although all classes are defined, not all the types are already implemented. See JSNX-1 and JSNX-2 issues.|
In addition to atomic simple types Jsonix supports list simple types. Such list types map an array of values to string (delimiter-separated items of the array).
Here's an example of a simple type which handles a list of doubles:
|Here's XML Schema analog:|
List types are declared using the Jsonix.Schema.XSD.List class:
|Built-in simple types typically declare a list-derived variant as MyType.INSTANCE.LIST (ex. Jsonix.Schema.XSD.Double.INSTANCE.LIST), so you don't need to create new instances of list types built-in types.|
Unlike XML Schema, Jsonix allows deriving list types from non-atomic types (ex. other list types). Below is an example of a list of lists of doubles:
This feature is planned but not supported, see JSNX-9.
Definition of complex types by restriction is not supported at the moment.
Type system in Jsonix is extensible, so if your requirements are not covered by the built-in simple types, you can write custom simple types to match your needs.
Jsonix requires an instance of a simple types to provide the following properties and functions:
- typeName - Qualified name of the type (as if you'd define it in an XML Schema). Optional.
- CLASS_NAME - String property which provides the name of the class. Required.
- unmarshal - Function which accepts Jsonix.Context and Jsonix.XML.Input and returns unmarshalled value.
- marshal - Function which accepts Jsonix.Context, value and Jsonix.XML.Output and marshalls the given value into the output.
In most cases you can just inherit from Jsonix.Schema.XSD.AnySimpleType and just implement print and parse methods. See Jsonix.Schema.XSD.Boolean for example:
Complex types are defined using the Jsonix.Model.ClassInfo class:
Complex type has a name and contains a number of properties. Properties can be provided on creation or added later on:
Complex types can be defined as extensions for other content types. This is achieved by setting the baseTypeInfo property of the extending type to point to the base type. Consider the following example:
In this example the base type has the properties alpha and beta whereas the extended type has four properties alpha, beta, gamma and delta. This corresponds to the following XML Schema:
Definition of complex types by restriction is not supported.
Jsonix allows you to map character content, attributes and elements using following property types:
- Character content
Property types enumerated above have different functionality. However, there are some basic characteristics shared by most properties.
Consider the following example:
The property named alpha is mapped to the element beta. So if we'll unmarshal the following element:
|Name of the property is also used by attribute, element and element reference properties to default the target XML attribute or element names if they are omitted.|
Element properties also have the cardinality characteristic; they can be collection or single properties.
Collection properties handle repeatable elements.
Consider this simple property:
This will unmarshal the following XML:
Now, we can make this property collection:
And then be able to process repeatable elements:
Some of the properties (namely Element reference/references and any element properties) can be declared as mixed. Mixed properties can handle elements together with character content. Consider the following example:
Here's an example of XML:
A common XML Schema design pattern is the usage of wrapper elements to enclose repeated elements, for instance:
The items element on its own has no meaning, it only encloses the item subelements. You can model such XML my using the wrapperElementName option in element properties:
Value property maps to the textual content of the XML element. It is defined using the Jsonix.Model.ValuePropertyInfo class:
See Defining complex type with simple content for usage example.
- Complex type can define at most one value property.
- Value property can be used with attribute or any attribute properties. It can not be used with:
Complex type with simple content can be defined with the help of the value property:
|Corresponding XML Schema would be:|
The type converts between the following XML:
Attribute property maps to the attribute of the XML element.
- Complex type can define at most one attribute property for the given attribute name.
"Any attribute" property maps to the attributes of the XML element.
Value of the property is a map of the form:
Where attributeX is the string representation of the qualified attribute name, valueX is the string value of the attribute.
For example, compare the following JSON:
With the equivalent XML:
- Complex type can define at most one "any attribute" property.
See Wrapper elements for an explanation of the wrapperElementName option.
Elements property is provided with elementTypeInfos, an array of element/type mappings. These mappings are objects carrying elementName, qualified name of the element and typeInfo, type of the element.
When unmarshalling an element, this property uses the name of the element to find the corresponding type and then uses this type for actual unmarshalling.
When marshalling a value, this property searches for a matching type for this value and then uses the corresponding element name to create the outgoing XML element.
For instance, the property declared above would handle the following XML:
As you see, we're getting elements of different types (strings, integers) from differently-named elements (string, integer) in one array property elements. The elementTypeInfos definition of our elements property allows Jsonix to understand that string elements must be unmarshalled as strings, integer elements - as integers. During marshalling, Jsonix tries to find a matching type (that is, a type for which this given value would be an instance of) and then use the corresponding element name for marshalling.
The "instance of" operator is implemented differently for simple and complex types.
Values of complex types are objects so there is no reliable way to determine if a given object is considered as "instance of" a certain complex type. To overcome this difficulty, objects may carry a special-purpose TYPE_NAME property, for instance:
Complex type thinks that value is an "instance of" itself if the value is an object and it has a string TYPE_NAME property matching the name of the complex type.
Element map property allows mapping one or more elements to an object/hashmap-valued property. Element map property is configured with two further properties which describe, what should be taken as a key of the hashmap and what as value.
Since version 1.1 element map properties can be collections. In this case, values in the hashmap will be arrays. This allows modelling multimaps.
XML elements for both properties is the same:
The advantage of this representation is that you can choose the name of the XML element dynamically:
Despite property name is still b, it will be marshalled as the element c:
Element reference property can be defined as follows:
Any element property handles unmarshalling as follows:
- When unmarshalling character data:
- If this property is mixed, character data is unmarshalled as string.
- Otherwise an error is reported.
- When unmarshalling an element:
- If this property allows typed objects, check if this element is know to the context via element mapping.
- If it is known, unmarshal as typed object.
- Otherwise if this property allows DOM, simply return current element as a DOM element.
- Otherwise report an error.
- If this property allows typed objects, check if this element is know to the context via element mapping.
Below is the correspondence between xs:any processing types and allowTypedObject/allowDom processing settings.
- Complex type may declare at most one "any element" property.
Consider the following property declaration:
The allowsDom, allowsTypedObject and mixed options are defaulted to true, so this property will produce:
- typed objects for elements known in this context;
- DOM nodes for elements which are not known to this context;
- strings for character data.
Assume we have declared the global elements string and value in our module:
- Download Jsonix.
- Add/import/include Jsonix scripts into your program/page.
- Write or generate Jsonix mappings.
- Create Jsonix context from these mappings.
- Create marshaller.
- Use marshalString, marshalDocument etc. methods of marshaller.
- Create unmarshaller.
- Use unmarshalString, unmarshalDocument, unmarshalURL etc. methods of unmarshaller.
In production you'll normally want to use the minified version of Jsonix:
- Jsonix-min.js - aggregated, minified version.
- Jsonix-all.js - aggregated, not minified version.
- lib/Jsonix.js - not aggregated, not minified development version.
In order to marshal or unmarshal you'll first need to create Jsonix context:
Jsonix context is a factory which produces marshallers or unmarshallers. Jsonix context is thread-safe and reusable.
Once Jsonix context is created you can use it to produce marshallers or unmarshallers:
Unlike the context itself, marshaller and unmarshallers neither thread-safe nor reusable.
Once you have a marshaller, you can marshal your object as XML:
Unmarshaller can parse your object from XML:
If you're already using XJC to compile your schemas, you'll just need to use the jsonix plugin for XJC. The plugin can be downloaded here. It is activated using the following command-line option:
- Set extension=true
- Add -Xjsonix to args/arg