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
  • Runs in node.js
  • 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, URLs or files (with node.js) as source
  • Driven by declarative 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 how it would look like in JavaScript:

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

Try it online in a fiddle.

Defining mappings

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

Basically, Jsonix mappings is a JavaScript object 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:

You can download the complete example.

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 objects which define how XML should be mapped to properties of objects and vice versa.
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:

See the Fiddle for this example.

Now we can enumerate basic components of Jsonix mappings:

These components will be described in the following sections.

Modules

Jsonix module is essentially just a simple JavaScript object which declares a set of XML/object mappings.

Fiddle.

Name

Each module must have a string name property which names a module. The name is useful for locally-named declarations. For instance, in the code below the full name of the PurchaseOrderType type info will be PO.PurchaseOrderType.

Fiddle.

Icon

For backwards-compatibility, name property the module is technically not required (you'll get no error if you pass a module without a name). However it is highly recommended to declare this property. We may implement in strict check for name in future versions.

Type infos

Each module may declare zero or more types using the typeInfos property.
Types are roughly equivalent to the global simple and complex types of the XML Schema.

Fiddle.

If type info is declared with a local name, it will get a "full" name based on the pattern <ModuleName>.<LocalName>, ex. PO.PurchaseOrderType.
See the types section for more information.

Element infos

Each module may declare zero or more element declarations.
Element declarations are roughly equivalent to global elements of the XML Schema.

Fiddle.

The mapping above basicaly says that <purchaseOrder .../> element should be processed using the PO.PurchaseOrderType type and <comment.../> using the (built-in) string type.

See the element declarations section for more information.

Default element and attribute namespaces

Element an attribute names can be declared using simple strings, for instance elementName: 'comment'. If you use namespaces (I hope you do), you can the use the defaultElementNamespaceURI or defaultAttributeNamespaceURI to declare the namespace for such names. Consider the following example of the mapping.

This will suit XML like:

Fiddle.

Icon

An alternative would have to declare the elementName like this:

Fiddle.
Which is a little bit more cumbersome.

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:

Fiddle.

The name property provides the name of the element to be mapped. This can be a qualified name defined by an object with properties namespaceURI, localPart (and maybe prefix) or a string. If name is given as a string, it will be resolved to the qualified name using the defaultNamespaceURI of the module.
See defining element and attribute names for details about name resolution. (TODO)

The typeInfo property defines the type which is associated with the given element. It can be a string (name of the type) or an object (full mapping of the type).
See referencing types for more information about type resolution. (TODO)

Icon

Element declaration has two more options, scope and substitutionHead which will be explained later on.

For example, consider the following element declaration:

This declaration maps the following element:

Onto the type MyModule.MyType. 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.).

Each type may have a name which can be used to reference this type in mappings.

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) which can be reference by name (ex. String). Below is the type mapping table:

XML Schema Type

Jsonix JavaScript Class

Jsonix Type Name

anySimpleType

Jsonix.Schema.XSD.AnySimpleType

AnySimpleType

string

Jsonix.Schema.XSD.String

String

normalizedString

Jsonix.Schema.XSD.NormalizedString

NormalizedString

token

Jsonix.Schema.XSD.Token

Token

language

Jsonix.Schema.XSD.Language

Language

Name

Jsonix.Schema.XSD.Name

Name

NCName

Jsonix.Schema.XSD.NCName

NCName

boolean

Jsonix.Schema.XSD.Boolean

Boolean

base64Binary

Jsonix.Schema.XSD.Base64Binary

Base64Binary

hexBinary

Jsonix.Schema.XSD.HexBinary

HexBinary

float

Jsonix.Schema.XSD.Float

Float

decimal

Jsonix.Schema.XSD.Decimal

Decimal

integer

Jsonix.Schema.XSD.Integer

Integer

nonPositiveInteger

Jsonix.Schema.XSD.NonPositiveInteger

NonPositiveInteger

negativeInteger

Jsonix.Schema.XSD.NegativeInteger

NegativeInteger

long

Jsonix.Schema.XSD.Long

Long

int

Jsonix.Schema.XSD.Int

Int

short

Jsonix.Schema.XSD.Short

Short

byte

Jsonix.Schema.XSD.Byte

Byte

nonNegativeInteger

Jsonix.Schema.XSD.NonNegativeInteger

NonNegativeInteger

unsignedLong

Jsonix.Schema.XSD.UnsignedLong

UnsignedLong

unsignedInt

Jsonix.Schema.XSD.UnsignedInt

UnsignedInt

unsignedShort

Jsonix.Schema.XSD.UnsignedShort

UnsignedShort

unsignedByte

Jsonix.Schema.XSD.UnsignedByte

UnsignedByte

positiveInteger

Jsonix.Schema.XSD.PositiveInteger

PositiveInteger

double

Jsonix.Schema.XSD.Double

Double

anyURI

Jsonix.Schema.XSD.AnyURI

AnyURI

QName

Jsonix.Schema.XSD.QName

QName

duration

Jsonix.Schema.XSD.Duration

Duration

dateTime

Jsonix.Schema.XSD.DateTime

DateTime

time

Jsonix.Schema.XSD.Time

Time

date

Jsonix.Schema.XSD.Date

Date

gYearMonth

Jsonix.Schema.XSD.GYearMonth

GYearMonth

gYear

Jsonix.Schema.XSD.GYear

GYear

gMonthDay

Jsonix.Schema.XSD.GMonthDay

GMonthDay

gDay

Jsonix.Schema.XSD.GDay

GDay

gMonth

Jsonix.Schema.XSD.GMonth

GMonth

Consider the following declaration of the global element:

This maps the following element:

Onto the following data:

Fiddle.

Icon

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

Icon

Since 2.0.11, Jsonix simple types always accept string when marshalling. Under the hood, they'll check if the provided value is a string. if it is, it will be parsed and then printen. So you can use '1234' as well as 1234 as an integer value. Note that the string value is not output directly, but parsed/printed to ensure the correct type and valid value.

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).
Derived type is defined as follows:

Example:

XML element:

Data:

Fiddle.

Icon

Here's XML Schema analog:

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:

You can use this type to convert between the string 0 0, 0 1, 1 1, 1 0, 0 0 and JavaScript array structure [ [ 0, 0 ], [ 0, 1 ], [ 1, 1 ], [ 1, 0 ], [ 0, 0 ] ].

Fiddle.

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:

  • name - Logical name of the type to be used in mappings.
  • 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 implement print and parse methods. See the following custom yes/no boolean typefor example:

Fiddle.

Complex types

Complex type has a name and contains a number of properties.

The name is defined using the localName property. The full name of the type will be composed of the name of the module and the local name of the complex type, with . as delimiter (MyModule.DataType in the example above. Name of the type can be used to reference this type in mappings (note the declaration of the data element above).

Properties are provided using the propertyInfos property. In the example above, we define two properties, key (mapped onto the key attribute) and value (mapped onto the textual contents of the element.

Complex types can be defined using the typeInfos property of the module:

Fiddle.

The mapping above converts between the following XML:

And the following JavaScript object:

Fiddle.

See #Properties for more information on defining properties.

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.

Turns into:

Next,

turns into:

Fiddle.

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 data is mapped to the element content. So if we'll unmarshal the following element:

We'll get the data property in the JavaScript object:

Fiddle.

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 the following collection property declaration:

This will unmarshal the following XML:

Into the following JavaScript object:

Fiddle.

Icon

Note that this is different from deriving types by list:

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:

Fiddle.

Wrapper elements

A common XML Schema design pattern is the usage of wrapper elements to enclose repeated elements, for instance:

The contents element on its own has no meaning, it only encloses the content subelements. You can model such XML my using the wrapperElementName option in element properties:

The XML sample above will be marshalled into the following JavaScript object:

Fiddle.

Defining properties

Value property

Property declaration syntax:

Value property maps to the textual content of the XML element:

XML:

JavaScript Object:

Fiddle.

Usage constraints:

Defining complex type with simple content

The value property can be used to define complex type with simple content. Consider the following XML Schema fragment:

The anonymous complex type within the root element is a complex type with simple content. This is how it can be mapped with Jsonix:

Fiddle.

Attribute property

Property declaration syntax:

Mapping example:

XML:

JavaScript object:

Fiddle.

Usage constraints:

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

Any attribute property

Property declaration syntax:

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

Mapping example:

XML:

JavaScript Object:

Fiddle.

Usage constraints:

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

Element property

Property declaration syntax:

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

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

Usage constraints:

Element property example - single element

Mapping example:

XML:

JavaScript object:

Fiddle.

Elements property

Property declaration syntax:

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, string 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.

Mapping example:

XML:

JavaScript object:

Fiddle.

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

Property declaration syntax:

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.

Mapping example:

XML:

JavaScript object:

Fiddle.

Element reference property

Property declaration syntax:

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:

Note that to do this trick we had to declare an element mapping for the element c:

This lets Jsonix know that the element c can substitute the element b and it should be processed as String.

Fiddle.

Scoped elements

There is one problem with element declaration above:

This makes c a global element. So you can now unmarshal the following XML:

This may or may not be the desired effect. To overcome this difficulty, you can define a scope for this element declaration.
Scope is essentially a complex type which will limit the applicability of the given element declaration. In the example above, we can limit the scope of the element c to the enclosing type MyModule.ElementRefType:

Fiddle.

Substitution groups

You might have noticed that although marshalling

worked as expected, unmarshalling

did not. The reason is that Jsonix sees the element c and can unmarshal it via the global element declaration, but it does not know which property it should be mapped to. Our complex type MyModule.ElementRefType only declares properties a and b.

To fix this, we can provide the substitutionHead property in the element declaration. The substitutionHead name the element (either via string or QName) which can be substituted by the given element. For instance, if we can define the element declaration as follows:

This will let Jsonix know that the element c substitutes the element b. And since our complex type MyModule.ElementRefType has an element reference property for the element b, Jsonix will know that the unmarshalled c should be assigned to the property b.

Fiddle.

Element references property

Property declaration syntax:

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

Example:

XML:

JavaScript object:

Fiddle.

Any element property

Property declaration syntax:

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 mapping example:

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.

XML:

Since we have declared global elements string and value in our module, these elements will be unmarshalled as typed objects. The element node is not known in this context - it will be returned as DOM. Character data three will be unmarshalled as string.

Fiddle.

Using Jsonix in your JavaScript program

  • Download Jsonix or install it with npm in node.js
  • Add/import/include/require 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 a web page

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 with RequireJS

Since 2.0.11.

Jsonix is compatible with AMD/RequireJS.

Include require.js in your page, point data-main to your main script file:

In your scripts, define Jsonix and mappings as dependencies of your module. The code below assumes Jsonix-all.js and PO.js are placed next to main.js and require.js in the js folder.

Note that Jsonix still works in node.js and in browser without RequireJS.

Installing Jsonix in node.js

Install:

Or add to dependencies of your package:

package.js

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:

Unmarshalling a file with node.js

Icon

Since 2.0

If you're running Jsonix in a node.js environment, you can also unmarshal from a file:

At the moment, the file will be loaded as a string, then parsed into DOM document and finally unmarshalled from the parsed document.

The optional argument options is passed directly to the fs.readFile(...) call. See node.js FileSystem API.

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.

Using command-line tool in node.js

Add schema generation as a preinstall script.

package.json

(Line breaks are added for readablility.)