1. Introduction

1.1. Welcome to Json-lib

JSON-lib is a java library for transforming beans, maps, collections, arrays and XML to JSON and back again to beans and DynaBeans. It is based on the work by Douglas Crockford in http://www.json.org/java.

The following table summarizes the types conversion between Java and JavaScript:

JSON Java

string

java.lang.String, java.lang.Character, char

number

java.lang.Number, byte, short, int, long, float, double

true/false

java.lang.Boolean, boolean

null

null

function

org.kordamp.json.JSONFunction

array

org.kordamp.json.JSONArray (object, string, number, boolean, function)

object

org.kordamp.json.JSONObject

The function type from JavaScript is not part of the JSON format "officially" (please refer to http://www.json.org) but it is supported as well.

Json-lib requires (at least) the following dependencies in your classpath:

  • Apache commons-lang3 3.9

  • Apache commons-beanutils 1.9.3

  • Apache commons-collections4 4.4

  • slf4j 1.7.30

  • ezmorph 3.0.0

Other dependencies are needed if working with XML:

  • xom 1.3.5

1.2. What is JSON

JSON (JavaScript Object Notation) is a lightweight data-interchange format. It is easy for humans to read and write. It is easy for machines to parse and generate. It is based on a subset of the JavaScript Programming Language, Standard ECMA-262 3rd Edition - December 1999. JSON is a text format that is completely language independent but uses conventions that are familiar to programmers of the C-family of languages, including C, C++, C#, Java, JavaScript, Perl, Python, and many others.

These properties make JSON an ideal data-interchange language.

Documentation for older releases can be found at json-lib.sourceforge.net/

2. Usage

2.1. Using the JSONSerializer

JSONSerializer can transform any java object to JSON notation and back with a simple and clean interface, leveraging all the builders in JSONObject and JSONArray. To transform a Java object into JSON use JSONSerializer.toJSON(). To transform a valid JSON value (by JSON, I mean an Object implementing that interface), use toJava(). The last method is an instance method because the serializer needs special configuration to transform a JSON value to a bean class, array, List or DynaBean.

2.2. Working with arrays and collections

The simplest way to create a JSONArray from a Java array or collection is through the static factory methods from JSONArray. JSONArray.fromObject() will inspect its parameter and call the correct factory or constructor.

Examples:

boolean[] boolArray = new boolean[]{true,false,true};
JSONArray jsonArray = JSONArray.fromObject( boolArray );
System.out.println( jsonArray );
// prints [true,false,true]
List list = new ArrayList();
list.add( "first" );
list.add( "second" );
JSONArray jsonArray = JSONArray.fromObject( list );
System.out.println( jsonArray );
// prints ["first","second"]
JSONArray jsonArray = JSONArray.fromObject( "['json','is','easy']" );
System.out.println( jsonArray );
// prints ["json","is","easy"]

2.3. Working with objects

2.3.1. From Beans and Maps to JSON

The simplest way to create a JSONObject from a bean or Map is through the static factory methods from JSONObject. JSONObject.fromObject() will inspect its parameter and call the correct factory or constructor.

Examples:

Map map = new HashMap();
map.put( "name", "json" );
map.put( "bool", Boolean.TRUE );
map.put( "int", new Integer(1) );
map.put( "arr", new String[]{"a","b"} );
map.put( "func", "function(i){ return this.arr[i]; }" );

JSONObject jsonObject = JSONObject.fromObject( map );
System.out.println( jsonObject );
// prints ["name":"json","bool":true,"int":1,"arr":["a","b"],"func":function(i){ return this.arr[i]; }]
class MyBean{
  private String name = "json";
  private int pojoId = 1;
  private char[] options = new char[]{'a','f'};
  private String func1 = "function(i){ return this.options[i]; }";
  private JSONFunction func2 = new JSONFunction(new String[]{"i"},"return this.options[i];");

// getters & setters
...
}

JSONObject jsonObject = JSONObject.fromObject( new MyBean() );
System.out.println( jsonObject );
/* prints
  {"name":"json","pojoId":1,"options":["a","f"],
  "func1":function(i){ return this.options[i];},
  "func2":function(i){ return this.options[i];}}
*/

CAUTION: when parsing, JSONObject and JSONArray will check for cycles in the hierarchy, throwing an exception if one is found. You can change this behavior by registering a CycleDetectionStrategy.

2.3.2. From JSON to Beans

Json-lib can transform JSONObjects to either a DynaBean or an specific bean class. <br/> When using DynaBean all arrays are converted to Lists, when using an specific bean class the transformation will use type conversion if necessary on array properties.

Convert to DynaBean:

String json = "{name=\"json\",bool:true,int:1,double:2.2,func:function(a){ return a; },array:[1,2]}";
JSONObject jsonObject = JSONObject.fromObject( json );
Object bean = JSONObject.toBean( jsonObject );
assertEquals( jsonObject.get( "name" ), PropertyUtils.getProperty( bean, "name" ) );
assertEquals( jsonObject.get( "bool" ), PropertyUtils.getProperty( bean, "bool" ) );
assertEquals( jsonObject.get( "int" ), PropertyUtils.getProperty( bean, "int" ) );
assertEquals( jsonObject.get( "double" ), PropertyUtils.getProperty( bean, "double" ) );
assertEquals( jsonObject.get( "func" ), PropertyUtils.getProperty( bean, "func" ) );
List expected = JSONArray.toList( jsonObject.getJSONArray( "array" ) );
Assertions.assertListEquals( expected, (List) PropertyUtils.getProperty( bean, "array" ) );

Convert to Bean:

String json = "{bool:true,integer:1,string:\"json\"}";
JSONObject jsonObject = JSONObject.fromObject( json );
BeanA bean = (BeanA) JSONObject.toBean( jsonObject, BeanA.class );
assertEquals( jsonObject.get( "bool" ), Boolean.valueOf( bean.isBool() ) );
assertEquals( jsonObject.get( "integer" ), new Integer( bean.getInteger() ) );
assertEquals( jsonObject.get( "string" ), bean.getString() );

There are two special cases when converting to an specific bean, if the target bean has a Map property and it must contain other beans, JSONObject.toBean() will transform the nested beans into DynaBeans. If you need those nested beans transformed into an specific class, you can either postprocess the Map attribute or provide hints on JSONObject’s attributes for conversion. JSONObject.toBean() may be passed a third argument, a Map, that will provide those hints. Every key must be either the name of a property or a regular expression matching the object’s properties, and the value must be a Class.

The second case is similar and it happens when the target bean has a Collection (List) as a property and it must contain other beans. In this case there is no way to provide hints for class conversion. The only possible solution is to postprocess the collection transforming each DynaBean into an specific bean.

To ease the postprocessing scenarios, EZMorph provides a Morpher capable of transforming a DynaBean into an specific bean, BeanMorpher

Example:

class MyBean{
  private List data;
  // getters &amp; setters
}

class Person{
  private String name;
  // getters &amp; setters
}

...

String json = "{'data':[{'name':'Wallace'},{'name':'Grommit'}]}";
Map classMap = new HashMap();
classMap.put( "data", Person.class );
MyBean bean = JSONObject.toBean( JSONObject.fromObject(json), MyBean.class, classMap );

This yields a MyBean instance that has DynaBeans inside the 'data' attribute', so now comes the postprocessing part, this can be done with an Iterator.

Example:

Morpher dynaMorpher = new BeanMorpher( Person.class, JSONUtils.getMorpherRegistry() );
morpherRegistry.registerMorpher( dynaMorpher );
List output = new ArrayList();
for( Iterator i = bean.getData().iterator(); i.hasNext(); ){
  output.add( morpherRegistry.morph( Person.class, i.next() ) );
}
bean.setData( output );

To learn more about Morphers, please visit EZMorph’s project site.

2.4. Working with XML

Working with XML has become easier since version 1.1. Transforming JSONObjects and JSONArrays from and to XML is done through the XMLSerializer.

2.4.1. From JSON to XML

Writing to JSON to XML is as simple as calling XMLSerializer.write(), but there are a lot of options that you may configure to get better control of the XML output. For example you may change the default names for the root element ('o' if object, 'a' if array), the default name for object (an object inside an array is "anonymous"), the default name for array (for the same reason as object), the default name for element (array items have no name). If you’d like to output namescape information but your JSON does not includes it, no problem, you have 8 methods that will let you register and manage namespaces; namespaces defined this way have precedence on any namespace declaration that may be inside the JSON. By default XMLSerializer will append special attributes to each xml element for easing the transformation back to JSON but you may configure it to skip appending those attributes. Any property on a JSONObject that begins with '@' will be treated as an attribute, any property named '#text' will be treated as a Text node.

Please review the javadoc for XMLSerializer to know more about the configurable options.

*Code* *XML output*
JSONObject json = new JSONObject( true );
String xml = XMLSerializer.write( json );
<o class="object" null="true">
JSONObject json = JSONObject.fromObject("{\"name\":\"json\",\"bool\":true,\"int\":1}");
String xml = XMLSerializer.write( json );
<o class="object">
  <name type="string">json</name>
  <bool type="boolean">true</bool>
  <int type="number">1</int>
</o>
JSONArray json = JSONArray.fromObject("[1,2,3]");
String xml = XMLSerializer.write( json );
<a class="array">
 <e type="number">1</e>
 <e type="number">2</e>
 <e type="number">3</e>
</a>

2.4.2. From XML to JSON

XMLSerializer treats each element as a string unless a type parameter is specified.
JSONFunction needs an additional parameter that specifies that function’s params.

All xml attributes will have the prefix '@' and text nodes will have the property name '#text'. XMLSerializer supports the rules outlined at Converting Between XML and JSON

*XML input* *Code*
<a class="array">
 <e type="function" params="i,j">
  return matrix[i][j];
 </e>
</a>
JSONArray json = (JSONArray) XMLSerializer.read( xml );
System.out.println( json );

CAUTION: when parsing, JSONObject and JSONArray will check for cycles in the hierarchy, throwing an exception if one is found. You can change this behavior by registering a CycleDetectionStrategy.

3. Build Configuration

3.1. Gradle

dependencies {
    compile 'org.kordamp.json:json-lib-core:3.0.2-SNAPSHOT'
}

3.2. Maven

<dependency>
    <groupId>org.kordamp.json</groupId>
    <artifactId>json-lib-core</artifactId>
    <version>3.0.2-SNAPSHOT</version>
</dependency>

4. Compatibility

The following lists summarizes the differences between Json-lib 3.x and 2.x

  • All classes have moved from package net.sf.json-lib to org.kordamp.json.

  • JDK 8 is the new binary base line.

  • Java Generics have been added to method signatures.

5. Using Json-lib with Groovy

Since version 2.0 Json-lib has integrated Groovy support, meaning that POGOs can be transformed into JSON and back, in the same manner as you do now with POJOs. There are other features as well:

  • Type transformations with the as keyword.
    A Groovy List (default type is ArrayList) can be transformed into a JSONArray.
    A Groovy Map (default type is HashMap) can be transformed into a JSONObject.
    A String can be transformed into JSON, JSONObject, JSONArray and JSONFunction.

def fromList = [1,true,'json'] as JSONarray
def fromMap = [integer:1, bool: true] as JSONObject
def strAsJson1 = "{integer:1, bool: true}" as JSON
def strAsJson2 = "[1,2,3]" as JSON
def strAsJsonObject = "{integer:1, bool: true}" as JSONObject
def strAsJsonArray = "[1,2,3]" as JSONArray
def strAsFunc = "function(param){ this.param = param; }" as JSONFunction
  • JSONObject supports the leftShift (<<) operator to append values, the following rules apply:

    • If the shifted arg is a Map, it will call putAll() on the object.

    • If the shifted arg is a List and its size == 2, the first element will be the key, and the second will be the value.

    • If the shifted arg is a List and its size > 2, the first element will be the key, the arg will be shifted by 1 and passed as the value (wi ll create a JSONArray because it is a List).

    • Any other type will be discarded, the object will not be affected nor an exception will be thrown.

  • JSONObject and JSONarray implement java.util.Comparable, which enables the use of the comparison operators with them (including the spaceship operator).

  • JSONObject implements java.util.Map and JSONArray implements java.util.List, anything you can do with Maps and List can be done with JSONObject and JSONArray.

  • JsonGroovyBuilder behaves like the JsonBuilder found in Grails but it will create a JSONObject/JSONArray instead of writing the built structure into a String.

Since version 2.2.1 Json-lib will not automatically enhance class through custom metaclasses, you must call GJson.enhanceClasses() before executing the examples shown on this page.

6. FAQ

6.1. If I use a LinkedHasMap to create a JSONObject the order is not preserved, why ?

The answer is in the JSON specification "An object is an unordered set of name/value pairs. An object begins with { (left brace) and ends with } (right brace). Each name is followed by : (colon) and the name/value pairs are separated by , (comma).".

JSONObject uses a HashMap for its properties representation because the order of its properties is not important.

6.2. Json-lib creates empty JSONObjects from my bean class, help me!

Json-lib uses the JavaBeans convention to inspect your beans and create JSONObjects. If the properties of your beans do not adhere to the convention, the resulting JSONObject will be empty or half empty. You must provide a read/write method pair for each property.

6.3. How do I configure Json-lib as a dependency with Maven2 ?

<dependency>
    <groupId>org.kordamp.json</groupId>
    <artifactId>json-lib-core</artifactId>
    <version>3.0.2-SNAPSHOT</version>
</dependency>

6.4. How can I transform a JSON string into a bean with an Enum property ?

You’ll have to register a Morpher that can handle your Enum. Json-lib conveniently includes such a Morpher, so the only thing left to do is configure it and register the Morpher into the MorpherRegistry as follows JSONUtils.getMorpherRegistry().registerMorpher(new EnumMorpher(MyEnum.class))

This step is optional since Json-lib 2.2

6.5. How can I transform a simple value into a complex one when serializing back to Java ?

Perhaps you’ve come across a scenario where you’ll want a JSON string like "{'id':1}" transformed back to a Java class, but the catch is that the id property is not a simple value but another class (a complex value), say a wrapper around a primitive long. If you try to serialize such a string back to Java you’ll get a ClassCastException. The solution to this problem is registering a Morpher that can handle the input and transform it into an instance of the expected type, in this case the class of the id property.

6.6. Json-lib does not find my inner bean, help!

In order for Json-lib (in fact the PropertyDescriptors) to find and inspect your beans they have to be declared public, and all desired properties must have a public pair of reader/writer. The same applies to beans declared as inner classes (which by the way must also be declared static). Its a good practice to define each bean in its own file, but if you can’t please make your inner beans public and static if possible.

6.7. I’d like to use XPath-like expressions with JSON, how do I do it ?

Use JXPath, which is a simple yet powerful project that enables XPath-like expressions to be used with java beans and maps.