Skip to end of metadata
Go to start of metadata

Introduction

Icon

Take a look at this question on Stackoverflow which incepted the development of Jsonix.

Jsonix (JSON interfaces for XML) is a JavaScript library which allows you to convert between XML and JSON structures.

With Jsonix you can parse XML into JavaScript objects (this process is called unmarshalling) or serialize JavaScript objects in XML form (this is called marshalling).

These conversions are based on simple XML/JSON mappings which can be written manually or generated from an XML Schema.

Icon

Strictly speaking, Jsonix works with JavaScript objects, which are not limited to JSON. But for the sake of simplicity we'll use JSON to denote these "plain old simple JavaScript objects".

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.

Related projects

JAXB

Jsonix is inspired by and based on JAXB which is a great tool to convert between XML and Java objects. Jsonix is literally a JAXB analog for JavaScript.

Jsonix mappings are heavily influenced by JAXB annotations.

Jsonix schema compiler is based on XJC, schema compiler from the JAXB Reference Implementation.

Features

  • Runs in almost any modern browser
  • Implements marshalling (serializing a JavaScript object info XML)
    • Supports string data and DOM nodes as result
  • Implements unmarshalling (parsing a JavaScript object from XML)
    • Supports string data, DOM nodes or URLs as source
  • Driven by XML/object mappings which control how JavaScript object is converted into XML or vice versa
  • Mappings can be automatically generated based on an XML Schema
  • Strongly-structured
    • XML/object mappings describe structures of JavaScript objects
  • Strongly-typed
    • Conversion between string content on XML side and values on the JavaScript side is controlled by declared property types.
  • 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

Working Example

This chapter demonstrates the usage of Jsonix in a classic "purchase order" example.

Assume you need to develop a JavaScript program which should process an XML in the following XML Schema:

Here's an example of XML for this schema:

Usage example

Here's how you would parse this XML document with Jsonix:

The callback function will receive the result of the unmarshalling in a form of a JavaScript object. Here's approximately how it would look like in a JSON form:

Here's how marshalling of a JavaScript object into XML would look like:

Defining mappings

Now let us take a look at the XML/object mappings, the part we skipped previously:

Basically, Jsonix mappings is a JavaScript program which describes how XML constructs (simple and complex types, elements, attributes) should be represented in object form. From the other hand, Jsonix mappings define the target object structure: objects, properties, their types and cardinalities. XML/object mappings are required to guarantee strongly-structured and strongly-typed mapping.

Generating mappings from an XML Schema

As we've seen above, Jsonix needs XML/object mappings to operate. These mappings can be created manually, they are just simple JavaScript programs which use Jsonix API.

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:

Mapping XML to JavaScript Objects

Jsonix needs XML/object mappings to operate. These mappings can be created manually or generated from an XML Schema. Either way, they are just simple JavaScript programs which use Jsonix API. This sections explains concepts of these mappings and describes how to create them.

Basic concepts

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

Modules

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:

Into the following JavaScript object:

Modules is a JavaScript object which contains a property named elementInfos. This property must be an array-valued property which lists element mappings declared by this module.

Apart from this required elementInfos property, modules may also declare types - just like the module One declared the type One.ValueType.

Icon

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).

Element declarations

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:

Icon

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.

Icon

However, you may also need to use MyModule.elementInfos to declare scoped elements.

Types

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 distinguished two categories of types: simple and complex types. The difference between them is that complex types contain properties whereas simple types don't.

Either way, types can convert between XML structures (elements, attributes, character data) and JavaScript structures (objects, arrays, strings, numbers etc.).

Simple types

Simple types convert between character data on XML side and primitive or basic types on the JavaScript side. For instance, Jsonix boolean type converts between "true" or "false" text on XML side and true or false boolean values on JavaScript:

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.

Built-in simple types

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:

To support this hierarchy, Jsonix declares an individual JavaScript class for each of these types. Each of the classes also has a pre-instantiated instance (ex. Jsonix.Schema.XSD.String.INSTANCE). Below is the type mapping table:

XML Schema Type

JavaScript Class

Instance

anySimpleType

Jsonix.Schema.XSD.AnySimpleType

Jsonix.Schema.XSD.AnySimpleType.INSTANCE

string

Jsonix.Schema.XSD.String

Jsonix.Schema.XSD.String.INSTANCE

normalizedString

Jsonix.Schema.XSD.NormalizedString

Jsonix.Schema.XSD.NormalizedString.INSTANCE

token

Jsonix.Schema.XSD.Token

Jsonix.Schema.XSD.Token.INSTANCE

language

Jsonix.Schema.XSD.Language

Jsonix.Schema.XSD.Language.INSTANCE

Name

Jsonix.Schema.XSD.Name

Jsonix.Schema.XSD.Name.INSTANCE

NCName

Jsonix.Schema.XSD.NCName

Jsonix.Schema.XSD.NCName.INSTANCE

boolean

Jsonix.Schema.XSD.Boolean

Jsonix.Schema.XSD.Boolean.INSTANCE

base64Binary

Jsonix.Schema.XSD.Base64Binary

Jsonix.Schema.XSD.Base64Binary.INSTANCE

hexBinary

Jsonix.Schema.XSD.HexBinary

Jsonix.Schema.XSD.HexBinary.INSTANCE

float

Jsonix.Schema.XSD.Float

Jsonix.Schema.XSD.Float.INSTANCE

decimal

Jsonix.Schema.XSD.Decimal

Jsonix.Schema.XSD.Decimal.INSTANCE

integer

Jsonix.Schema.XSD.Integer

Jsonix.Schema.XSD.Integer.INSTANCE

nonPositiveInteger

Jsonix.Schema.XSD.NonPositiveInteger

Jsonix.Schema.XSD.NonPositiveInteger.INSTANCE

negativeInteger

Jsonix.Schema.XSD.NegativeInteger

Jsonix.Schema.XSD.NegativeInteger.INSTANCE

long

Jsonix.Schema.XSD.Long

Jsonix.Schema.XSD.Long.INSTANCE

int

Jsonix.Schema.XSD.Int

Jsonix.Schema.XSD.Int.INSTANCE

short

Jsonix.Schema.XSD.Short

Jsonix.Schema.XSD.Short.INSTANCE

byte

Jsonix.Schema.XSD.Byte

Jsonix.Schema.XSD.Byte.INSTANCE

nonNegativeInteger

Jsonix.Schema.XSD.NonNegativeInteger

Jsonix.Schema.XSD.NonNegativeInteger.INSTANCE

unsignedLong

Jsonix.Schema.XSD.UnsignedLong

Jsonix.Schema.XSD.UnsignedLong.INSTANCE

unsignedInt

Jsonix.Schema.XSD.UnsignedInt

Jsonix.Schema.XSD.UnsignedInt.INSTANCE

unsignedShort

Jsonix.Schema.XSD.UnsignedShort

Jsonix.Schema.XSD.UnsignedShort.INSTANCE

unsignedByte

Jsonix.Schema.XSD.UnsignedByte

Jsonix.Schema.XSD.UnsignedByte.INSTANCE

positiveInteger

Jsonix.Schema.XSD.PositiveInteger

Jsonix.Schema.XSD.PositiveInteger.INSTANCE

double

Jsonix.Schema.XSD.Double

Jsonix.Schema.XSD.Double.INSTANCE

anyURI

Jsonix.Schema.XSD.AnyURI

Jsonix.Schema.XSD.AnyURI.INSTANCE

QName

Jsonix.Schema.XSD.QName

Jsonix.Schema.XSD.QName.INSTANCE

duration

Jsonix.Schema.XSD.Duration

Jsonix.Schema.XSD.Duration.INSTANCE

dateTime

Jsonix.Schema.XSD.DateTime

Jsonix.Schema.XSD.DateTime.INSTANCE

time

Jsonix.Schema.XSD.Time

Jsonix.Schema.XSD.Time.INSTANCE

date

Jsonix.Schema.XSD.Date

Jsonix.Schema.XSD.Date.INSTANCE

gYearMonth

Jsonix.Schema.XSD.GYearMonth

Jsonix.Schema.XSD.GYearMonth.INSTANCE

gYear

Jsonix.Schema.XSD.GYear

Jsonix.Schema.XSD.GYear.INSTANCE

gMonthDay

Jsonix.Schema.XSD.GMonthDay

Jsonix.Schema.XSD.GMonthDay.INSTANCE

gDay

Jsonix.Schema.XSD.GDay

Jsonix.Schema.XSD.GDay.INSTANCE

gMonth

Jsonix.Schema.XSD.GMonth

Jsonix.Schema.XSD.GMonth.INSTANCE

Icon

Although all classes are defined, not all the types are already implemented. See JSNX-1 and JSNX-2 issues.

Deriving simple types by list

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:

Icon

Here's XML Schema analog:

List types are declared using the Jsonix.Schema.XSD.List class:

Icon

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:

Deriving simple types by union

This feature is planned but not supported, see JSNX-9.

Deriving simple types by restriction

Definition of complex types by restriction is not supported at the moment.

Defining custom simple types

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

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:

Properties declared in a complex type define both the structure of the JavaScript object as well as structure of the XML it will be mapped onto.

Defining complex types by extension

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:

Here's a couple of examples of XML and equivalent JavaScript objects

Defining complex types by restriction

Definition of complex types by restriction is not supported.

Properties

Properties define contents of a complex type. From one hand, they configure the structure of a JavaScript object which is mapped by this complex type. From the other hand, they describe, how this object will be presented in an XML form.

Jsonix allows you to map character content, attributes and elements using following property types:

Basic property characteristics

Property types enumerated above have different functionality. However, there are some basic characteristics shared by most properties.

Property name

Every property must have a name (string). Primary function of this name is to define the name of the matching JavaScript object property.

Consider the following example:

The property named alpha is mapped to the element beta. So if we'll unmarshal the following element:

We'll get an alpha property in the JavaScript object:

Icon

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.

Property cardinality

Element properties also have the cardinality characteristic; they can be collection or single properties.

Icon

Value, attribute and any attribute properties are always single.

Collection properties handle repeatable elements.

Consider this simple property:

This will unmarshal the following XML:

Into the following JavaScript object:

Now, we can make this property collection:

And then be able to process repeatable elements:

JavaScript property value will in this case be an array:

Mixed properties

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:

And the equivalent JavaScript object:

Wrapper elements

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:

Below is the equivalent JavaScript object.

Defining properties

Value property

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.

Usage constraints:

Defining complex type with simple content

Complex type with simple content can be defined with the help of the value property:

Icon

Corresponding XML Schema would be:

The type converts between the following XML:

And JavaScript object:

Attribute property

Attribute property maps to the attribute of the XML element.

Usage constraints:

  • Complex type can define at most one attribute property for the given attribute name.

Any attribute property

"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:

Usage constraints:

  • Complex type can define at most one "any attribute" property.

Element property

Element property maps a JavaScript object property onto an XML element.

See #Wrapper elements for an explanation of the wrapperElementName option.

Usage constraints:

Element property example - single element
Property declaration
Sample XML
JavaScript object

Elements property

Elements property maps several XML elements onto one JavaScript object property.

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:

The corresponding JavaScript object would be:

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.

For simple types the "instance of" operator checks that value has an appropriate JavaScript type (like, string for strings, number for numeric types, boolean for booleans and so on). Simple type also checks that value is actually allowed (ex. integers are round numbers, bytes are in range from -128 to +127 and so on). This can surely be ambiguous, so caution is advised when mixing compatible simple types in one elements property.

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

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.

Example:

Property declarations
Sample XML
JavaScript object

Element reference property

Element reference property maps a JavaScript object property onto XML element. This is similar to the element property, however what's different is content representation in the JavaScript object. Consider the following properties:

XML elements for both properties is the same:

However, content representation in the JavaScript object is different:

In the example above the element reference property b is represented in the JavaScript object by the following construct:

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:

Scoped elements

TODO

Substitution groups

TODO

Element references property

Element references property maps several XML elements onto one JavaScript object property. This is similar to elements property, but for references.

Any element property

Any element property can handle arbitrary XML elements. Depending on the processing types and whether these elements are known within the current Jsonix context, you'll get objects, DOM nodes or strings on the JavaScript side.

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.

Below is the correspondence between xs:any processing types and allowTypedObject/allowDom processing settings.

Processing type

allowTypedObject

allowDom

lax

(plus)

(plus)

strict

(plus)

(minus)

skip

(minus)

(plus)

Usage constraints:

  • Complex type may declare at most one "any element" property.
Any element property example - lax processing

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:

Compare the sample XML and the equivalent JavaScript object:

Sample XML
JavaScript object

Using Jsonix in your JavaScript program

  • Download Jsonix.
  • Add/import/include Jsonix scripts into your program/page.
  • Write or generate Jsonix mappings.
  • Create Jsonix context from these mappings.
    • To marshal (serialize JavaScript objects as XML):
      • Create marshaller.
      • Use marshalString, marshalDocument etc. methods of marshaller.
    • To unmarshal (parse JavaScript objects from XML):
      • Create unmarshaller.
      • Use unmarshalString, unmarshalDocument, unmarshalURL etc. methods of unmarshaller.

Including Jsonix scripts

In production you'll normally want to use the minified version of Jsonix:

Available versions:

  • Jsonix-min.js - aggregated, minified version.
  • Jsonix-all.js - aggregated, not minified version.
  • lib/Jsonix.js - not aggregated, not minified development version.

Using Jsonix

Creating Jsonix Context

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.

Marshalling

Once you have a marshaller, you can marshal your object as XML:

Unmarshalling

Unmarshaller can parse your object from XML:

Generating mappings from XML Schema

Icon

You need a Java environment to generate mappings from XML Schemata.

Command-line tool

XJC plugin

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:

Maven usage

  • Set extension=true
  • Add -Xjsonix to args/arg

Ant usage

Bindings files

Now you may wonder, what the bindings file does. Bindings customize schema compilation. For instance, you can instruct Jsonix schema compiler to generate the PO module (by default written to the PO.js file).

Here's how a typical bindings file looks like.

This bindings file basically says two things:

  • The purchaseorder.xsd schema will get the package org.hisrc.jsonix.demos.po
  • The package org.hisrc.jsonix.demos.po will have the associated space name (module) PO

This might look a bit cumbersome, but this is due to certain limitations in the underlying technologies.

  • No labels