Project Nayuki

JSON library (Java)


This is Nayuki’s library for parsing and serializing JSON data, open-source and written in Java. It is compact at roughly 1000 lines of code in 2 source files, and provides a small, easy-to-use API.

I created this library because while browsing the web, I couldn’t find a Java JSON library that suited my needs. The libraries I encountered had problems like large code bases (e.g. 3000 lines of code in 10 files), multiple dependencies, wrapper objects around every piece of data, Java reflection magic, etc.

This library converts between standard Java objects (Integer, Double, String, List, Map, some others) and JSON data as Unicode strings. The overall concept was inspired by the straightforwardness of Python’s built-in json library.

My JSON library provides two static methods serialize() and parse(), a set of path-based accessor methods like getInt(), one custom data structure JsonNumber, and a few convenience I/O methods to read from file/URL or write to file. It does not provide any other wrappers, factories, or configuration. It only has a dependency on Java SE classes.


Full package: nayuki-json-lib.jar (source code, compiled classes, Javadoc HTML)

Individual source files:


import io.nayuki.json.Json;
import io.nayuki.json.JsonNumber;

/* Parsing and queries */

Object dec = Json.parse("[99, 88, 77]");
int    a = Json.getInt   (dec, 0);  // 99
long   b = Json.getLong  (dec, 1);  // 88L
double c = Json.getDouble(dec, 2);  // 77.0

Object dec = Json.parse(
    "{\"a\":\"alpha\", \"b\":[\"beta\",\"bravo\",\"buck\"]}");
String a  = Json.getString(dec, "a");     // alpha
String b0 = Json.getString(dec, "b", 0);  // beta
String b1 = Json.getString(dec, "b", 1);  // bravo
String b2 = Json.getString(dec, "b", 2);  // buck

/* Numbers */

Object orig = 1234;                  // Integer
String enc = Json.serialize(orig);   // 1234
Object dec = Json.parse(enc);        // JsonNumber
int val = Json.getInt(dec);          // 1234
int val = ((Number)dec).intValue();  // 1234

Object orig = 3.1415;               // Double
String enc = Json.serialize(orig);  // 3.1415 (due to Double.toString())
Object dec = Json.parse(enc);       // JsonNumber
double val = Json.getDouble(dec);   // 3.1415
double val = ((Number)dec).doubleValue();  // 3.1415

Object orig = Double.NaN;           // Double
String enc = Json.serialize(orig);  // IllegalArgumentException: Cannot encode

Object orig = BigInteger.ONE.shiftLeft(192);  // 2^192
String enc = Json.serialize(orig);
// enc == 6277101735386680763835789423207666416102355444464034512896
Object dec = Json.parse(enc);              // JsonNumber
BigInteger val = ((JsonNumber)dec).bigIntegerValue();  // 6277...2896 (exact)
double val = ((Number)dec).doubleValue();  // 6.277101735386681e57
long   val = ((Number)dec).longValue();    // NumberFormatException: Overflow

/* Strings */

Object orig = "hello";              // String
String enc = Json.serialize(orig);  // "hello" (output has quotes)
Object dec = Json.parse(enc);       // String
String val = Json.getString(dec);   // hello
String val = (String)dec;           // hello

Object orig = "你好" + (char)10;     // String
String enc = Json.serialize(orig);  // "\u4f60\u597d\n" (with quotes)
Object dec = Json.parse(enc);       // String
String val = Json.getString(dec);   // 你好\n
String val = (String)dec;           // 你好\n

/* Lists and maps */

List<Object> orig = new ArrayList<>();
String enc = Json.serialize(orig);  // []
orig.add(new ArrayList<Object>());
String enc = Json.serialize(orig);  // [4, "N", []]
Object dec = Json.parse(enc);       // List<Object>
List<Object> val = Json.getList(dec);
List<Object> val = (List<Object>)dec;

Map<String,Object> orig = new TreeMap<>();
orig.put("a", 7);
orig.put("b", 9);
orig.put("c", 6);
orig.put("d", 8);
String enc = Json.serialize(orig);  // {"a":7, "b":9, "c":6, "d":8}
Object dec = Json.parse(enc);       // SortedMap<String,Object>
Map<String,Object> val = Json.getMap(dec);
Map<String,Object> val = (Map<String,Object>)dec;


class Json in package io.nayuki.json

Provides static methods to convert between Java objects and JSON text, and also to query JSON data structures.

0, 1.2, -3.4e+5java.lang.Number / io.nayuki.json.JsonNumber
"abc"java.lang.String / java.lang.CharSequence

See the full details and caveats in serialize() and parse().

public static String serialize(Object obj)

Serializes the specified Java object / tree of objects into a JSON text string.

There are a number of restrictions on the input object/tree:

Note that all Unicode strings can be encoded to JSON. This includes things like embedded nulls (U+0000), as well as characters outside the Basic Multilingual Plane (over U+FFFF).

The returned string is pure ASCII with no control characters, i.e. all characters are in the range [0x20, 0x7E]. This means that all ASCII control characters and above-ASCII Unicode characters are escaped, and there are no tabs or line breaks in the output string.

  • obj – the object/tree to serialize to JSON (can be null)
  • a JSON text string representing the given object/tree (not null)
  • IllegalArgumentException – if any of the restrictions are violated

public static Object parse(String str)

Parses the specified JSON text into a Java object / tree of objects. Notes:

  • str – the JSON text string (not null)
  • the object/tree (can be null) corresponding to the JSON data
  • IllegalArgumentException – if the JSON text fails to conform to the standard syntax in any manner or an object has a duplicate key

public static Object getObject(Object root, Object... path)

Traverses the specified JSON object/tree along the specified path through maps and lists, and returns the object at that location. The path is a (possibly empty) sequence of strings or integers.

(The related methods getInt(), getLong(), getFloat(), getDouble(), getString(), getList(), getMap() behave similarly.)

For example, in this data structure:

data = {
  "alpha": null,
  "beta" : [9, 88, 777],
  "gamma": ["x", 3.21,
      "y": 5,
      "z": 6

The following queries produce these results:

getObject(data): Map[alpha=null, beta=List[9, 88, 777], gamma=List["x", 3.21, Map[y=5, z=6]]]
getObject(data, "alpha"): null
getObject(data, "beta"): List[9, 88, 777]
getObject(data, "beta", 0): 9
getObject(data, "beta", 1): 88
getObject(data, "beta", 2): 777
getObject(data, "beta", 3): IndexOutOfBoundsException
getObject(data, "charlie"): IllegalArgumentException (no such key)
getObject(data, "gamma"): List["x", 3.21, Map[y=5, z=6]]
getObject(data, "gamma", 0): "x"
getObject(data, "gamma", 1): 3.21
getObject(data, "gamma", 2): Map[y=5, z=6]
getObject(data, "gamma", 2, "y"): 5
getObject(data, "gamma", 2, "z"): 6
getObject(data, "gamma", "2"): IllegalArgumentException (map expected)
getObject(data, 0): IllegalArgumentException (list expected)

  • root – the JSON object/tree to query
  • path – the sequence of strings and integers that expresses the query path
  • the object at the location (can be null)
  • IllegalArgumentException – if a map/list was expected but not found, or a map key was not found, or a path component is not a string/integer
  • IndexOutOfBoundsException – if a list index is negative or greater/equal to the list length
  • NullPointerException – if any argument or path component is null (except if the root is null and the path is zero-length

public static Object parseFromFile(File file)
public static Object parseFromUrl(URL url)
public static void serializeToFile(Object obj, File file)

These are convenience methods for parse(String) and serialize(Object), and their behavior should be self-explanatory.