Skip to content

Latest commit

 

History

History
1017 lines (751 loc) · 43.7 KB

README.md

File metadata and controls

1017 lines (751 loc) · 43.7 KB

JSONx Framework for Java

JSON Schema for the enterprise

Build Status Coverage Status Javadocs Released Version Snapshot Version

Submodule Summary
runtime API to bind Java classes to JSON objects whose structure is expressed in the
JSON Schema Definition Language.
generator Utility to generate Java binding classes from a JSD(x) schema.
validator Utility to validate JSON documents against a JSD(x) schema.
jsonx-maven-plugin Maven plugin to generate and convert JSONx and JSD(x) bindings.
jaxrs JAX-RS @Provider to read and write JSON documents with the JSONx Runtime API.
jsonxml Utility to convert and validate JSON and JSONx documents.
sample Sample applications.

Abstract

The JSONx Framework for Java provides a reference implementation processor, validator, and runtime API for the JSON Schema Definition Language (JSD), which is a schema language for JSON designed in close resemblance to the XMLSchema specification. The framework also provides a collection of structural and functional patterns intended to help developers work with JSON documents.

This document introduces the JSONx Framework for Java, and presents a directory of links to its constituent parts and related resources.

Table of Contents

  1 Introduction
    1.1 Conventions Used in This Document
  2 Use-Cases
    2.1 Consumer Driven Contracts
  3 JSON Schema Definition Language
    3.1 Purpose
    3.2 Requirements
    3.3 Getting Started
    3.4 JSD vs JSDx
    3.5 Specification
  4 JSONx Runtime API
    4.1 Purpose
    4.2 Requirements
    4.3 Getting Started
    4.4 Specification
  5 JSONx Binding Generator
    5.1 Purpose
    5.2 Requirements
    5.3 Getting Started
       5.3.1 Generator
       5.3.2 Converter
    5.4 Specification
  6 JSONx Validator
    6.1 Purpose
    6.2 Requirements
    6.3 Getting Started
       6.3.1 Valid Case
       6.3.2 Invalid Case
    6.4 Specification
  7 JSONx Integration for JAX-RS
    7.1 Purpose
    7.2 Requirements
    7.3 Getting Started
    7.4 Specification
  8 JSONx Maven Plugin
    8.1 Purpose
    8.2 Requirements
    8.3 Getting Started
    8.4 Specification
  9 JsonXml
    9.1 Purpose
    9.2 Requirements
    9.3 Getting Started
       9.3.1 JSON-to-XML
       9.3.2 XML-to-JSON
    9.4 Specification
  10 Contributing
  11 Special Thanks
  12 License

1 Introduction

The JSONx Framework for Java was created to help developers address common problems and use-cases when working with JSON documents. The JSONx Framework for Java offers structural and functional patterns that systematically reduce errors and pain-points commonly encountered when developing software that interfaces with JSON. The structural patterns are defined in the JSON Schema Definition Language, which is a programming-language-agnostic schema language used to describe constraints and document the meaning, usage and relationships of the constituent parts of JSON documents. The functional patterns are reference implementations of the specification of the schema language, providing utilities that address common use-cases for applications that use JSON in one way or another. Common use-cases include:

  1. Definition of a normative contract between a producer and consumer of JSON documents.
  2. Validation of JSON documents conforming to a respective schema document.
  3. Java class runtime API for JSON documents conforming to a respective schema document.

1.1 Conventions Used in This Document

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC2119.

2 Use-Cases

The following sections lists common use-cases where JSONx Framework for Java can be used.

2.1 Consumer Driven Contracts

The JSONx Framework for Java was created specifically with Consumer Driven Contracts in mind. With the JSON Schema Definition Language (JSD), one can create a Consumer Driven Contract (CDC) with a model that includes the capacity to evolve based on schema versioning. Additionally, the JSD can be used by producers and consumers to validate documents in a communication protocol.

The following example illustrates a simple protocol that uses the CDC approach, and consists of the actors:

  1. Producer: Representing the provider of the ProductSearch service.
  2. Consumer1: The first consumer of the ProductSearch service.
  3. Consumer2: The second consumer of the ProductSearch service.

Consider a simple ProductSearch service, which allows consumer applications to search a product catalogue.

Version v1 of the protocol defines the contract:

  • Request

    GET /ProductSearch?name=<name>
    
  • Response

    {
      "Version": "v1",
      "CatalogueID": <number>,
      "Name": <string>,
      "Price": <string>,
      "Manufacturer": <string>,
      "InStock": <boolean>
    }

The schema that describes the Response contract is:

JSD
{
  "jx:ns": "http://www.jsonx.org/schema-0.4.jsd",
  "jx:schemaLocation": "http://www.jsonx.org/schema-0.4.jsd http://www.jsonx.org/schema.jsd",

  "product": { "jx:type": "object", "abstract": true, "properties": {
    "CatalogueID": { "jx:type": "number", "range": "[1,]", "scale": 0, "nullable": false},
    "Name": { "jx:type": "string", "pattern": "\\S|\\S.*\\S", "nullable": false },
    "Price": { "jx:type": "string", "pattern": "\\$\\d+\\.\\d{2}", "nullable": false },
    "Manufacturer": { "jx:type": "string", "pattern": "\\S|\\S.*\\S", "nullable": false },
    "InStock": { "jx:type": "boolean", "nullable": false} } },

  "product1": { "jx:type": "object", "extends": "product", "properties": {
    "Version": { "jx:type": "string", "pattern": "v1", "nullable": false } } }
}
JSDx
<schema
  xmlns="http://www.jsonx.org/schema-0.4.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.jsonx.org/schema-0.4.xsd http://www.jsonx.org/schema.xsd">

  <object name="product" abstract="true">
    <property name="CatalogueID" xsi:type="number" range="[1,]" scale="0" nullable="false"/>
    <property name="Name" xsi:type="string" pattern="\S|\S.*\S" nullable="false"/>
    <property name="Price" xsi:type="string" pattern="\$\d+\.\d{2}" nullable="false"/>
    <property name="Manufacturer" xsi:type="string" pattern="\S|\S.*\S" nullable="false"/>
    <property name="InStock" xsi:type="boolean" nullable="false"/>
  </object>

  <object name="product1" extends="product">
    <property name="Version" xsi:type="string" pattern="v1" nullable="false"/>
  </object>

</schema>

Note: The Converter utility automatically converts between JSD and JSDx.

All actors -- Producer, Consumer1, and Consumer2 -- agree on the contract, and implement and integrate the protocol into their systems. To assert receipt of contract-compliant documents, all actors use the contract definition to automatically validate received and sent messages.

After many months of running in production, Consumer2 issues a request to the Producer to provide additional information in the response. Specifically, Consumer2 requests for the addition of another field in the JSON response:

{
- "Version": "v1.0",
+ "Version": "v2.0",
  "CatalogueID": <number>,
  "Name": <string>,
  "Price": <string>,
  "Manufacturer": <string>,
  "InStock": <boolean>,
+ "Description": <string>
}

To satisfy Consumer2's request, the contract is updated to support version v2 of the Response:

JSD
{
  "jx:ns": "http://www.jsonx.org/schema-0.4.jsd",
  "jx:schemaLocation": "http://www.jsonx.org/schema-0.4.jsd http://www.jsonx.org/schema.jsd",

  "product": { "jx:type": "object", "abstract": true, "properties": {
    "CatalogueID": { "jx:type": "number", "range": "[1,]", "scale": 0, "nullable": false},
    "Name": { "jx:type": "string", "pattern": "\\S|\\S.*\\S", "nullable": false },
    "Price": { "jx:type": "string", "pattern": "\\$\\d+\\.\\d{2}", "nullable": false },
    "Manufacturer": { "jx:type": "string", "pattern": "\\S|\\S.*\\S", "nullable": false },
    "InStock": { "jx:type": "boolean", "nullable": false} } },

  "product1": { "jx:type": "object", "extends": "product", "properties": {
    "Version": { "jx:type": "string", "pattern": "v1", "nullable": false } } }

+ "product2": { "jx:type": "object", "extends": "product", "properties": {
+   "Version": { "jx:type": "string", "pattern": "v2", "nullable": false },
+   "Description": { "jx:type": "string", "pattern": "\\S|\\S.*\\S", "nullable": false } } }
}
JSDx
<schema
  xmlns="http://www.jsonx.org/schema-0.4.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.jsonx.org/schema-0.4.xsd http://www.jsonx.org/schema.xsd">

  <object name="product" abstract="true">
    <property name="CatalogueID" xsi:type="number" range="[1,]" scale="0" nullable="false"/>
    <property name="Name" xsi:type="string" pattern="\S|\S.*\S" nullable="false"/>
    <property name="Price" xsi:type="string" pattern="\$\d+\.\d{2}" nullable="false"/>
    <property name="Manufacturer" xsi:type="string" pattern="\S|\S.*\S" nullable="false"/>
    <property name="InStock" xsi:type="boolean" nullable="false"/>
  </object>

  <object name="product1" extends="product">
    <property name="Version" xsi:type="string" pattern="v1" nullable="false"/>
  </object>

+ <object name="product2" extends="product">
+   <property name="Version" xsi:type="string" pattern="v2" nullable="false"/>
+   <property name="Description" xsi:type="string" pattern="\S|\S.*\S" nullable="false"/>
+ </object>

</schema>

Note: The Converter utility automatically converts between JSD and JSDx.

With this approach, the v2 evolution of the contract satisfies Customer2. And, since the contract also retains support for v1, integration with Customer1 is unaffected.

For the application code, see Sample: Consumer Driven Contracts.

3 JSON Schema Definition Language

Describes JSON documents using schema components to constrain and document the meaning, usage and relationships of their constituent parts: value types and their content.

3.1 Purpose

Provide a schema language to describe normative contracts between producer and consumer ends of a protocol exchanging JSON documents.

3.2 Requirements

  1. The schema language MUST constrain and document the meaning, usage, constraints and relationships of the constituent parts of a JSON document.

  2. The schema language MUST provide meaningful and useful constraint rules for the 5 JSON value types:

    boolean, number, string, object, array.

  3. The schema language MUST support schema descriptions for any and all legal JSON documents, as specified by RFC2119.

  4. The schema language MUST be free-of and agnostic-to patterns specific to any particular programming language.

  5. The schema language MUST be able to describe itself.

3.3 Getting Started

The JSON Schema Definition Language can be expressed in 2 forms: JSD (Json Schema Document), and JSDx (JSD in XML semantics).

Create schema.jsd or schema.jsdx with the following content:

JSD
{
  "jx:ns": "http://www.jsonx.org/schema-0.4.jsd",
  "jx:schemaLocation": "http://www.jsonx.org/schema-0.4.jsd http://www.jsonx.org/schema.jsd",

  "myNumber": { "jx:type": "number", "range": "[-1,1)" },
  "myString": { "jx:type": "string", "pattern": "[a-z]+" },
  "myObject": {
    "jx:type": "object", "properties": {
      "myArray": {
        "jx:type": "array", "elements": [
          { "jx:type": "boolean" },
          { "jx:type": "reference", "type": "myNumber" },
          { "jx:type": "reference", "type": "myString" },
          { "jx:type": "array", "elements": [
            { "jx:type": "boolean" },
            { "jx:type": "number", "range": "[0,100]", "scale": 0 },
            { "jx:type": "string", "pattern": "[0-9]+" },
            { "jx:type": "any", "types": "myNumber myString" } ]},
        { "jx:type": "reference", "type": "myObject" },
        { "jx:type": "any", "types": "myString myObject" }]
      }
    }
  }
}
JSDx
<schema
  xmlns="http://www.jsonx.org/schema-0.4.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.jsonx.org/schema-0.4.xsd http://www.jsonx.org/schema.xsd">

  <number name="myNumber" range="[-1,1)"/>
  <string name="myString" pattern="[a-z]+"/>
  <object name="myObject">
    <property name="myArray" xsi:type="array">
      <boolean/>
      <reference type="myNumber"/>
      <reference type="myString"/>
      <array>
        <boolean/>
        <number range="[0,100]" scale="0"/>
        <string pattern="[0-9]+"/>
        <any types="myNumber myString"/>
      </array>
      <reference type="myObject"/>
      <any types="myString myObject"/>
    </property>
  </object>

</schema>

Note: You can use the Converter utility to automatically convert between JSD and JSDx.

This example defines a schema with 3 types that express the following structure:

  1. Type myNumber: A number between the range -1 (inclusive) and 1 (exclusive).
  2. Type myString: A string with the regex patter "[a-z]+".
  3. Type myObject: An object that has one property:
    1. "myArray": An array that defines a sequence of elements:
      1. boolean
      2. myNumber
      3. myString
      4. An array with the following elements:
        1. boolean
        2. An integer number between 0 and 100.
        3. A string of pattern "[0-9]+"
        4. Either myNumber or myString.
      5. myObject
      6. Either myString or myObject.

3.4 JSD vs JSDx

The JSDx format offers XML validation, and using an XML IDE like oXygen XML Editor offers edit-time XML validation, such as:

When using the JSDx format with the oXygen XML Editor, the auto-completion features of the editor will guide you in writing the schema. With the JSDx format, the XML editor will also validate keys and keyrefs to ensure that declared types are referenced correctly.

3.5 Specification

For a detailed specification of the schema language, see JSON Schema Definition Language.

4 JSONx Runtime API

Provides a way for JSON objects whose structure is expressed in the JSON Schema Definition Language to be validated, parsed and marshaled, to and from Java objects of strongly-typed classes.

4.1 Purpose

Provide a runtime API for parsing and marshaling JSON documents to and from strongly-typed Java classes.

4.2 Requirements

  1. The runtime API MUST be able to model the full scope of normative meaning, usage, constraints and relationships of the constituent parts of a JSON document as specifiable with the schema language.

  2. The runtime API MUST enforce (via validation) the full scope of normative meaning, usage, constraints and relationships of the constituent parts of a JSON document as specifiable in the schema language.

  3. The runtime API MUST produce clear and useful error messages when exception of schema document constraints are encountered during validation of JSON documents.

  4. The runtime API MUST constrain the constituent parts of a schema document to Java type bindings that are as lightweight as necessary to retain the full normative scope of specification of the schema language.

  5. The runtime API MUST use light coupling, not imposing requirements for exclusionary patterns onto a class model of binding classes.

  6. The runtime API MUST offer easy patterns for manual description of bindings.

  7. The runtime API MUST be straightforward, intuitive, and resilient to human error.

4.3 Getting Started

The JSONx Runtime API uses annotations to bind class definitions to usage, constraints and relationships specifiable in the schema language.

The following illustrates usage of the runtime API with an example of an invoice.

  1. Create invoice.jsd or invoice.jsdx in src/main/resources/:

JSD
{
  "jx:ns": "http://www.jsonx.org/schema-0.4.jsd",
  "jx:schemaLocation": "http://www.jsonx.org/schema-0.4.jsd http://www.jsonx.org/schema.jsd",

  "money": { "jx:type": "number", "range": "[0,]", "scale": 2},
  "positiveInteger": { "jx:type": "number", "range": "[1,]", "scale": 0},
  "date": { "jx:type": "string", "pattern": "-?\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(02-(0[1-9]|1\\d|2\\d))|((0[469]|11)-(0[1-9]|[12]\\d|30)))" },
  "nonEmptyString": { "jx:type": "string", "pattern": "\\S|\\S.*\\S" },
  "address": { "jx:type": "object", "properties": {
    "name": { "jx:type": "reference", "nullable": false, "type": "nonEmptyString" },
    "address": { "jx:type": "reference", "nullable": false, "type": "nonEmptyString" },
    "city": { "jx:type": "reference", "nullable": false, "type": "nonEmptyString" },
    "postalCode": { "jx:type": "reference", "nullable": false, "type": "nonEmptyString", "use": "optional" },
    "country": { "jx:type": "reference", "type": "nonEmptyString" } }
  },
  "invoice": { "jx:type": "object", "properties": {
    "number": { "jx:type": "reference", "type": "positiveInteger" },
    "date": { "jx:type": "reference", "type": "date" },
    "billingAddress": { "jx:type": "reference", "type": "address" },
    "shippingAddress": { "jx:type": "reference", "type": "address" },
    "billedItems": { "jx:type": "array", "nullable": false, "elements": [
      { "jx:type": "reference", "type": "item" } ] } }
  },
  "item": { "jx:type": "object", "properties": {
    "description": { "jx:type": "reference", "nullable": false, "type": "nonEmptyString" },
    "code": { "jx:type": "reference", "nullable": false, "type": "positiveInteger" },
    "quantity": { "jx:type": "reference", "nullable": false, "type": "positiveInteger" },
    "price": { "jx:type": "reference", "nullable": false, "type": "money" } }
  }
}
JSDx
<schema
  xmlns="http://www.jsonx.org/schema-0.4.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.jsonx.org/schema-0.4.xsd http://www.jsonx.org/schema.xsd">

  <number name="money" range="[0,]" scale="2"/>
  <number name="positiveInteger" range="[1,]" scale="0"/>
  <string name="date" pattern="-?\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\d|3[01])|(02-(0[1-9]|1\d|2\d))|((0[469]|11)-(0[1-9]|[12]\d|30)))"/>
  <string name="nonEmptyString" pattern="\S|\S.*\S"/>
  <object name="address">
    <property name="name" xsi:type="reference" type="nonEmptyString" nullable="false"/>
    <property name="address" xsi:type="reference" type="nonEmptyString" nullable="false"/>
    <property name="city" xsi:type="reference" type="nonEmptyString" nullable="false"/>
    <property name="postalCode" xsi:type="reference" type="nonEmptyString" nullable="false" use="optional"/>
    <property name="country" xsi:type="reference" type="nonEmptyString"/>
  </object>
  <object name="invoice">
    <property name="number" xsi:type="reference" type="positiveInteger"/>
    <property name="date" xsi:type="reference" type="date"/>
    <property name="billingAddress" xsi:type="reference" type="address"/>
    <property name="shippingAddress" xsi:type="reference" type="address"/>
    <property name="billedItems" xsi:type="array" nullable="false">
      <reference type="item"/>
    </property>
  </object>
  <object name="item">
    <property name="description" xsi:type="reference" type="nonEmptyString" nullable="false"/>
    <property name="code" xsi:type="reference" type="positiveInteger" nullable="false"/>
    <property name="quantity" xsi:type="reference" type="positiveInteger" nullable="false"/>
    <property name="price" xsi:type="reference" type="money" nullable="false"/>
  </object>

</schema>

Note: You can use the Converter utility to automatically convert between JSD and JSDx.

  2. With the invoice.jsd or invoice.jsdx, you can use the jsonx-maven-plugin to automatically generate the Java class files. In your POM, add:

<plugin>
  <groupId>org.jsonx</groupId>
  <artifactId>jsonx-maven-plugin</artifactId>
  <version>0.4.0</version>
  <executions>
    <execution>
      <goals>
        <goal>generate</goal>
      </goals>
      <phase>generate-sources</phase>
      <configuration>
        <destDir>${project.build.directory}/generated-sources/jsonx</destDir>
        <prefix>com.example.invoice.</prefix>
        <schemas>
          <schema>src/main/resources/invoice.jsd</schema> <!-- or invoice.jsdx -->
        </schemas>
      </configuration>
    </execution>
  </executions>
</plugin>

  3. (Alternatively) Create the Java class files by hand:

Note: Set-ters and get-ters have been replaced with public fields for conciseness.

import org.jsonx.*;

public class Address implements JxObject {
  @StringProperty(pattern="\\S|\\S.*\\S", nullable=false)
  public String name;

  @StringProperty(pattern="\\S|\\S.*\\S", nullable=false)
  public String address;

  @StringProperty(pattern="\\S|\\S.*\\S", nullable=false)
  public String city;

  @StringProperty(pattern="\\S|\\S.*\\S", use=Use.OPTIONAL, nullable=false)
  public String postalCode;

  @StringProperty(pattern="\\S|\\S.*\\S")
  public String country;
}
import org.jsonx.*;

public class Item implements JxObject {
  @StringProperty(pattern="\\S|\\S.*\\S", nullable=false)
  public String description;

  @NumberProperty(range="[1,]", scale=0, nullable=false)
  public long code;

  @NumberProperty(range="[1,]", scale=0, nullable=false)
  public long quantity;

  @NumberProperty(range="[1,]", scale=2, nullable=false)
  public java.math.BigDecimal price;
 }
import org.jsonx.*;

public class Invoice implements JxObject {
  @NumberProperty(range="[1,]", scale=0)
  public Long number;

  @StringProperty(pattern="-?\\d{4}-((0[13578]|1[02])-(0[1-9]|[12]\\d|3[01])|(02-(0[1-9]|1\\d|2\\d))|((0[469]|11)-(0[1-9]|[12]\\d|30)))")
  public String date;

  @ObjectProperty
  public Address billingAddress;

  @ObjectProperty
  public Address shippingAddress;

  @ObjectElement(id=0, type=Item.class)
  @ArrayProperty(elementIds={0}, nullable=false)
  public java.util.List<Item> billedItems;
}

  4. You can use these classes to represent Addresses, Items, and Invoices.

Address address = new Address();
address.name = "John Doe";
address.address = "111 Wall St.";
address.city = "New York";
address.postalCode = "10043";
address.country = "USA";

Item item = new Item();
item.code = 123;
item.description = "Pocket Protector";
item.price = new BigDecimal("14.99");
item.quantity = 5;

Invoice invoice = new Invoice();
invoice.number = 14738L;
invoice.date = "2019-05-13";
invoice.billingAddress = address;
invoice.shippingAddress = address;
invoice.billedItems = Collections.singletonList(item);

  5. You can now marshal the Java objects to JSON:

String json = JxEncoder._2.marshal(invoice);
System.out.println(json);

... will produce:

{
  "number": 14738,
  "date": "2019-05-13",
  "billingAddress": {
    "name": "John Doe",
    "address": "111 Wall St.",
    "city": "New York",
    "postalCode": "10043",
    "country": "USA"
  },
  "shippingAddress": {
    "name": "John Doe",
    "address": "111 Wall St.",
    "city": "New York",
    "postalCode": "10043",
    "country": "USA"
  },
  "billedItems": [{
    "description": "Pocket Protector",
    "code": 123,
    "quantity": 5,
    "price": 14.99
  }]
}

  6. You can also parse the JSON into Java objects:

Invoice invoice2 = JxDecoder.parseObject(Invoice.class, new JsonReader(new StringReader(json)));
assertEquals(invoice, invoice2);

For the application code, see Sample: Invoice.

4.4 Specification

For a detailed specification of the runtime API, see JSONx Runtime API.

5 JSONx Binding Generator

Consumes a JSD schema, and generates classes that use the JSONx Runtime API to achieve binding between JSON documents conforming to a JSD schema, and Java object represetations of these documents.

5.1 Purpose

Provide a binding generator utility for automatic generation of binding classes from a schema document.

5.2 Requirements

  1. The binding generator MUST be able to consume a schema document, and produce Java class definitions (.java files) that use the runtime API.

  2. The binding generator MUST be able to consume Java class definitions (.class files) utilizing the runtime API, and produce a schema document.

  3. The binding generator MUST create Java classes (.java files) that encode the full normative scope of the schema document.

  4. The binding generator MUST represent the constituent parts of a schema document with Java type bindings that are as strongly-typed as possible, but not limiting in any way with regard to the definition of the respective constituent part.

  5. The binding generator MUST be able to validate a schema document.

5.3 Getting Started

The JSONx Binding Generator provides convenience utilities for generating bindings and converting schema documents. The following illustrates example usage of the Generator and Converter executable classes.

5.3.1 Generator

The following example generates binding classes (.java files) in target/generated-sources/jsonx for the schema document at src/main/resources/example.jsd, with prefix org.example$.

java -cp ... org.jsonx.Generator --prefix org.example$ -d target/generated-sources/jsonx src/main/resources/example.jsd

5.3.2 Converter

The following example converts the JSD file at src/main/resources/example.jsd to a JSDx file in target/generated-resources.

java -cp ... org.jsonx.Converter src/main/resources/example.jsd target/generated-resources/example.jsdx

5.4 Specification

For a detailed specification of the binding generator, see JSONx Binding Generator.

6 JSONx Validator

The JSONx Validator consumes a JSD schema and a JSON document, and returns successfully if the specified documents are valid against the provided schema, or fails with an exception specifying the validation error if the specified document is invalid against the provided schema. The JSONx Validator utilizes the JSONx Binding Generator to perform its validation.

6.1 Purpose

Provide a validator utility for automatic validation of JSON documents against a schema document.

6.2 Requirements

  1. The validator MUST be able to consume a JSON document, and validate its contents agains a provided schema document.

  2. The validator MUST return successfully if a JSON document conforms to the provided schema document.

  3. The validator MUST throw an exception specifying the validation error if a JSON document does not conform to the provided schema document.

6.3 Getting Started

The JSONx Validator provides convenience utilities for validating a JSON document agains a schema document. The following illustrates example usage of the Validator.

6.3.1 Valid Case

The following example uses the Validator to validate a valid JSON document against a schema document.

$ java -cp ... org.jsonx.Validator src/main/resources/example.jsd src/main/resources/valid.json
$ echo $?
0

6.3.2 Invalid Case

The following example uses the Validator to validate an invalid JSON document against a schema document.

$ java -cp ... org.jsonx.Validator src/main/resources/example.jsd src/main/resources/invalid.json
Invalid content was found in empty array: @org.jsonx.StringElement(id=0, pattern="[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}"): Content is not complete
$ echo $?
1

6.4 Specification

The Validator is a utility class that can be used on the CLI to validate a JSON document against a schema document. The Validator class has the following usage specification:

Usage: Usage: Validator <SCHEMA> <JSON...>
Supported SCHEMA formats:
                 <JSD|JSDx>

7 JSONx Integration for JAX-RS

Implements the MessageBodyReader and MessageBodyWriter interfaces in the JAX-RS API to integrate with JAX-RS server runtimes.

7.1 Purpose

Provide JSONx Integration for JAX-RS for parsing and marshaling Java object instances of binding classes in a JAX-RS runtime.

7.2 Requirements

  1. The JSONx Integration for JAX-RS MUST support validation of JSON upon the consumption and production of documents in a JAX-RS runtime.

  2. The JSONx Integration for JAX-RS MUST support any JAX-RS application that implements the facets relevant to parsing and marshaling of entity object, as defined in the JAX-RS 2.0 Specification.

  3. The JSONx Integration for JAX-RS MUST be automatic and free of any configuration that would couple an application to the JSONx Framework for Java.

7.3 Getting Started

The JSONx Integration for JAX-RS sub-project provides a Provider implementing the MessageBodyReader and MessageBodyWriter interfaces that can be registered with a JAX-RS runtime.

The following illustrates example usage.

  1. Create account.jsd or account.jsdx in src/main/resources/.

JSD
{
  "jx:ns": "http://www.jsonx.org/schema-0.4.jsd",
  "jx:schemaLocation": "http://www.jsonx.org/schema-0.4.jsd http://www.jsonx.org/schema.jsd",

  "uuid": { "jx:type": "string", "pattern": "[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}" },
  "id": { "jx:type": "object", "abstract": true, "properties": {
    "id": { "jx:type": "reference", "nullable": false, "type": "uuid" } } },
  "credentials": { "jx:type": "object", "properties": {
    "email": { "jx:type": "string", "nullable": false, "pattern": "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,6}" },
    "password": { "jx:type": "string", "nullable": false, "pattern": "[0-9a-f]{64}", "use": "optional" } } },
  "account": { "jx:type": "object", "extends": "credentials", "properties": {
    "id": { "jx:type": "reference", "nullable": false, "type": "uuid", "use": "optional" },
    "firstName": { "jx:type": "string", "nullable": false },
    "lastName": { "jx:type": "string", "nullable": false } } }
}
JSDx
<schema
  xmlns="http://www.jsonx.org/schema-0.4.xsd"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.jsonx.org/schema-0.4.xsd http://www.jsonx.org/schema.xsd">

  <string name="uuid" pattern="[0-9A-F]{8}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{4}-[0-9A-F]{12}"/>
  <object name="id" abstract="true">
    <property name="id" xsi:type="reference" type="uuid" nullable="false"/>
  </object>
  <object name="credentials">
    <property xsi:type="string" name="email" pattern="[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}" nullable="false"/>
    <property xsi:type="string" name="password" pattern="[0-9a-f]{64}" use="optional" nullable="false"/>
  </object>
  <object name="account" extends="credentials">
    <property name="id" xsi:type="reference" type="uuid" nullable="false" use="optional"/>
    <property name="firstName" xsi:type="string" nullable="false"/>
    <property name="lastName" xsi:type="string" nullable="false"/>
  </object>

</schema>

Note: You can use the Converter utility to automatically convert between JSD and JSDx.

  2. Add the org.jsonx:jsonx-maven-plugin to the POM.

<plugin>
  <groupId>org.jsonx</groupId>
  <artifactId>jsonx-maven-plugin</artifactId>
  <version>0.4.0</version>
  <executions>
    <execution>
      <goals>
        <goal>generate</goal>
      </goals>
      <configuration>
        <destDir>${project.build.directory}/generated-sources/jsonx</destDir>
        <prefix>com.example.jsonx.</prefix>
        <schemas>
          <schema>src/main/resources/account.jsonx</schema>
        </schemas>
      </configuration>
    </execution>
  </executions>
</plugin>

  3. Upon successful execution of the jsonx-maven-plugin plugin, Java class files will be generated in generated-sources/jsonx. Add this path to your Build Paths in your IDE to integrate into your project.

The generated classes can be instantiated as any other Java objects. They are strongly typed, and will guide you in proper construction of a JSON message. The following APIs can be used for parsing and marshalling JSONx to and from JSON:

To parse JSON to JSONx Bindings:

String json = "{\"email\":\"john@doe\",\"password\":\"066b91577bc547e21aa329c74d74b0e53e29534d4cc0ad455abba050121a9557\"}";
Credentials credentials = JxDecoder.parseObject(Credentials.class, new JsonReader(new StringReader(json)));

To marshal JSONx Bindings to JSON:

String json2 = JxEncoder.get().marshal(credentials);
assertEquals(json, json2);

  4. Next, register the JxObjectProvider provider in the JAX-RS appilcation singletons, and implement the AccountService:

public class MyApplication extends javax.ws.rs.core.Application {
  @Override
  public Set<Object> getSingletons() {
    return Collections.singleton(new JxObjectProvider(JxEncoder._2));
  }
}

@Path("/account")
@RolesAllowed("registered")
public class AccountService {
  @GET
  @Produces("application/vnd.example.v1+json")
  public Account get(@Context SecurityContext securityContext) {
    Account account = new Account();
    ...
    return account;
  }

  @POST
  @Consumes("application/vnd.example.v1+json")
  public void post(@Context SecurityContext securityContext, Account account) {
    ...
  }
}

7.4 Specification

For a detailed specification of JSONx Integration for JAX-RS, see JSONx Integration for JAX-RS.

8 JSONx Maven Plugin

A Maven plugin for generating JSONx and JSD bindings.

8.1 Purpose

Provide schema validation, schema conversion, and Java binding source generation in a Maven plugin.

8.2 Requirements

  1. The JSONx Maven plugin MUST offer utilities for the generation of Java binding sources from a specified schema document.

  2. The JSONx Maven plugin MUST offer utilities for validation of schema documents.

  3. The JSONx Maven plugin MUST offer utilities for conversion of schema documents from JSD to JSDx, and vice versa.

  4. The JSONx Maven plugin MUST present clear and informative errors and warnings that arise during parsing and validation of schema documents and JSON documents with an associated schema.

8.3 Getting Started

The JSONx Maven Plugin implements a Maven MOJO that can be used in a pom.xml. The following illustrates an example usage.

<plugin>
  <groupId>org.jsonx</groupId>
  <artifactId>jsonx-maven-plugin</artifactId>
  <version>0.4.0</version>
  <executions>
    <execution>
      <goals>
        <goal>generate</goal>
      </goals>
      <phase>generate-sources</phase>
      <configuration>
        <destDir>${project.build.directory}/generated-sources/jsonx</destDir>
        <prefix>com.example.json.</prefix>
        <schemas>
          <schema>src/main/resources/schema.jsd</schema> <!-- or schema.jsdx -->
        </schemas>
      </configuration>
    </execution>
  </executions>
</plugin>

8.4 Specification

For a detailed specification of the Maven plugin, see JSONx Maven Plugin.

9 JsonXml

Offers facilities for converting JSON documents to XML, and vice-versa.

9.1 Purpose

Provide an encoding of JSON documents in an analogous form that uses XML semantics, referred to as JsonXml documents.

9.2 Requirements

  1. The JsonXml documents MUST be able to represent any and all legal JSON documents, as specified by RFC2119.

  2. The JsonXml documents MUST be translatable to JSON documents, and vice versa, preserving all normative and non-normative features of the original document.

  3. The JsonXml documents MUST provide meaningful and useful validation features via XSD validation.

9.3 Getting Started

The JsonXml sub-project provides convenience utilities for converting JSON documents to XML. The following illustrates example usage of the JxConverter class.

9.3.1 JSON-to-XML

String xml = JxConverter.jsonToXml(new JsonReader(new FileReader("example.json")));

9.3.2 XML-to-JSON

String json = JxConverter.xmlToJson(new FileInputStream("example.xml"));

9.4 Specification

For a detailed specification of JsonXml, see JsonXml.

10 Contributing

Pull requests are welcome. For major changes, please open an issue first to discuss what you would like to change.

Please make sure to update tests as appropriate.

11 Special Thanks

Java Profiler
Special thanks to EJ Technologies for providing their award winning Java Profiler (JProfiler) for development of the JSONx Framework.

12 License

This project is licensed under the MIT License - see the LICENSE.txt file for details.