json-io can be used directly on JSON Strings or with Java's Streams.
Example 1: Java object graph to JSON String
Employee emp;
// Emp fetched from database
String json = JsonIo.toJson(emp, writeOptions);
This example will convert the Employee
instance to a JSON String, including nested sub-objects. If
JsonIo.toObjects(json, readOptions, Employee.class)
were used on this JSON String,
a new Java Employee
instance would be returned.
See WriteOptions reference for list of all WriteOptions
and instructions on how to use.
Example 2: String to Java object
String json = // String JSON representing Employee instance
Employee employee = JsonIo.toObjects(json, readOptions, Employee.class);
This will convert the JSON String back to a Java Object graph.
See ReadOptions reference for a list of all ReadOptions
and instructions on how to use.
Example 3: Java Object to OutputStream
Employee emp;
// emp obtained from data store...
JsonIo.toJson(outputStream, emp, writeOptions);
In this example, a Java object is written to an OutputStream
in JSON format. The stream is closed when finished. If
you need to keep the OutputStream
open (e.g. NDJSON), then set writeOptions.closeStream(false).
Example:
WriteOptions writeOptions = new WriteOptionsBuilder().closeStream(false).build();
JsonIo.toJson(outputStream, record1, writeOptions);
JsonIo.toJson(outputStream, record2, writeOptions);
...
JsonIo.toJson(outputStream, recordn, writeOptions);
outputStream.close();
Example 4: InputStream
to Java object
Employee emp = JsonIo.toObjects(stream, readOptions, Employee.class);
In this example, anInputStream
is supplying the JSON.
json-io provides the choice to use the generic Map
of Maps
representation of an object, akin to a Javascript
associative array. When reading from a JSON String
orInputStream
of JSON, use JsonIo:
String json = // or InputStream to JSON providing source
ReadOptions readOptions = new ReadOptionsBuilder().returnAsNativeJsonObjects().build();
Map root = JsonIo.toObjects(json, readOptions, Map.class);
See the ReadOptions
below for the feature control options. In the provided example, rather than returning the objects
converted into Java classes, the raw JSON values are parsed and returned as Maps
. This forms a graph consisting of all
Map
instances, arrays, and primitive types.
When Map
is returned, the root value can represent one of the following:
- JSON Object (
{...}
): Transformed into aMap
that represents any JSON object{...}
. - JSON Array (
[...]
): Represented as aMap
with a key of@items
which holds the list representing the JSON array[...]
. - JSON Primitive: Such as boolean (true/false), null, numbers (long, double), and strings, directly represented as their Java equivalents.
This Map
representation can be rewritten to a JSON String or Stream, ensuring that the output JSON will match the
original input JSON stream. This feature is especially useful for handling JSON strings or streams containing class
references not present in the JVM parsing the JSON. It allows complete reading and potential manipulation of the content,
followed by rewriting the String or stream, providing a robust solution for dynamic data handling.
Sometimes you will run into a class that does not want to serialize. On the read-side, this can be a class that does
not want to be instantiated easily. A class that has private constructors, constructor with many difficult to supply
arguments, etc. There are unlimited Java classes 'out-there' that json-io
has never seen. It can instantiate many classes, and
resorts to a lot of "tricks" to make that happen. However, if a particular class is not instantiating, add a
JsonReader.ClassFactory
(one that you write, which subclasses this interface) and associate it to the class you want to
instantiate. See examples for how to do this.
JsonReader.ClassFactory // Create a class that implements this interface
JsonWriter.JsonClassWriter // Create a class that implements this interface
Your JsonReader.ClassFactory
class is called after the JSON is parsed and json-io
is converting all the Maps to
Java instances. Your factory class is passed the JsonObject (a Map) with the fields and values from the JSON so that
you can create your class and populate it at the same time. Use the Resolver
to load complex fields
of your class (Non-primitives, Object[]'s, typed arrays, Lists, Maps), making things easy - you only have to worry about
the primitives in your class (see the examples below for how to 'tee up' the Resolver
to load the sub-graph for
you.)
The code examples below show how to tell json-io
to associate your CustomFactory
and CustomWriter
to particular classes. Often you don't need to resort to a CustomFactory,
however, when you run into that one difficult class, these tools allow you to breeze through creating, reading, and
writing it. The WriteOptions Reference and ReadOptions Reference
have lots of additional information for Read/Write options.
Example Code - Primitive fields
Example Code - Primitive and non-primitive fields (sub-object)
Example Code - Primitive, array, type array, List, Map
- Used during writing (
JsonWriter
) and reading (JsonReader
) - Primarily for shortening class names in JSON output
- Example:
java.math.BigInteger = BigInteger
- Lightweight, just changes the string representation
- Doesn't affect class loading or behavior
- Used during class instantiation (
Resolver
) - Changes actual class used for instantiation
- Example:
java.util.RegularEnumSet = java.util.EnumSet
- More invasive as it affects the actual type created
- Should be used sparingly, only when:
- Handling internal implementation classes (like
RegularEnumSet
) - Managing backward compatibility with older serialized forms
- Dealing with JDK implementation details that shouldn't leak into JSON
- Handling internal implementation classes (like
- Used during object instantiation
- Controls how instances are created and populated
- Most flexible and powerful mechanism
- Proper place for custom instantiation logic
- Examples:
EnumSetFactory,
CollectionFactory,
etc.
- Used for special serialization/deserialization logic
- Can completely override normal processing
- Most complex but most powerful
- Strongly recommended: Use
ClassFactory
instead of aCustomReader
as it creates and loads.
Included is a small Javascript utility (jsonUtil.js
in the root folder) that will take a JSON output
stream created by the JSON writer and substitute all@ref's
for the actual pointed to object. It's a one-line
call -resolveRefs(json)
. This will substitute@ref
tags in the JSON for the actual pointed-to (@id
) object.
Even though json-io is great for Java / Javascript serialization, here are some other uses for it:
Many projects use JsonIo
to write an object to JSON, then read it in, cloning the original object graph:
Employee emp;
// emp obtained from somewhere...
Employee deepCopy = (Employee) JsonIo.deepCopy(emp, null, null); // ReadOptions, WriteOptions can be null
Instead of System.out.println()
debugging, call JsonIo.toJson(obj, writeOptions)
and dump the JSON
string out. That will give you the full referenceable graph dump in JSON. Use the prettyPrint feature of WriteOptions
to make the JSON more human-readable.