Jsonj: a new library for working with JSon in Java

2011-05-31

Update 11 July 2011: I’ve done several commits on github since announcing the project here. New features have been added; bugs have been fixed; you can find jsonj on maven central now as well; etc. In short, head over to github for the latest on this.

I’ve just uploaded a weekend project to github. So, here it is, jsonj. Enjoy.

If you read my post on json a few weeks ago, you may have guessed that I’m not entirely happy with the state of json in Java relative to other languages that come with native support for json. I can’t fix that entirely but I felt I could do a better job than most other frameworks I’ve been exposed to.

So, I sat down on Sunday and started pushing this idea of just taking the Collections framework and combining that with the design of the Json classes in GSon, which I use at work currently, and throwing in some useful ideas that I’ve applied at work. The result is a nice, compact little framework that does most of what I need it to do. I will probably add a few more features to it and expand some of the existing ones. I use some static methods at work that I can probably do in a much nicer way in this framework.

Here is some example usage (note, this will likely not stay in sync with the code in github, check there for latest version):

Updated to version 0.3, check here for latest version

/**
 * Copyright (c) 2011, Jilles van Gurp
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 */
package org.jsonj;

import static org.jsonj.tools.JsonBuilder.array;
import static org.jsonj.tools.JsonBuilder.nullValue;
import static org.jsonj.tools.JsonBuilder.object;
import static org.jsonj.tools.JsonBuilder.primitive;
import static org.jsonj.tools.JsonSerializer.serialize;
import static org.jsonj.tools.JsonSerializer.write;
import static org.testng.Assert.assertTrue;

import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.LinkedList;

import org.jsonj.tools.JsonParser;
import org.testng.annotations.Test;

/**
 * Not really a test but a nice place to show off some how to use this.
 */
@Test
public class ShowOffTheFramework {

  /** this could be a singleton or a spring injected object, threadsafe of course. */
  private final JsonParser jsonParser = new JsonParser();;

  public void whatCanThisBabyDo() throws IOException {
    JsonObject object = object()
      .put("its", "just a hash map")
      .put("and", array(
        primitive("adding"),
        primitive("stuff"),
        object().put("is", "easy").get(),
        array("another array")))
      .put("numbers", 42)
      .put("including_this_one", 42.0)
      .put("booleanstoo", true)
      .put("nulls_if_you_insist", nullValue())
      .put("a", object().put("b", object().put("c", true).put("d", 42).put("e", "hi!").get()).get())
      .put("array",
        array("1", "2", "etc", "varargs are nice")).get();

    assertTrue(object instanceof LinkedHashMap, "JsonObject actually extends LinkedHashMap");

    // get with varargs, a natural evolution for Map
    assertTrue(object.get("a","b","c").asPrimitive().asBoolean(), "extract stuff from a nested object");
    assertTrue(object.getBoolean("a","b","c"), "or like this");
    assertTrue(object.getInt("a","b","d") == 42, "or an integer");
    assertTrue(object.getString("a","b","e").equals("hi!"), "or a string");

    assertTrue(object.getArray("array").isArray(), "works for arrays as well");
    assertTrue(object.getObject("a","b").isObject(), "and objects");

    // builders are nice, but still feels kind of repetitive
    JsonObject anotherObject = object.getOrCreateObject("1","2","3","4");
    anotherObject.put("5", "xxx");
    assertTrue(object.getString("1","2","3","4","5").equals("xxx"),"yep, we just added a string value 5 levels deep");
    JsonArray anotherArray = object.getOrCreateArray("5","4","3","2","1");
    anotherArray.add("xxx");
    assertTrue(object.getArray("5","4","3","2","1").contains("xxx"),"naturally works for arrays too");

    // Lets do some other stuff
    assertTrue(object.equals(object),
      "equals is implemented as a deep equals");
    assertTrue(array("a", "b").equals(array("b", "a")),
      "mostly you shouldn't care about the order of stuff in json");
    assertTrue(
      object().put("a", 1).put("b", 2).get()
      .equals(
        object().put("b", 2).put("a", 1).get()),
      "true for objects as well");

    // Arrays are lists
    JsonArray array = array("foo", "bar");
    assertTrue(array instanceof LinkedList, "JsonArray extends LinkedList");
    assertTrue(array.get(1) == array.get("bar"), "returns the same object");
    assertTrue(array.contains("foo"), "obviously");
    assertTrue(array.contains(primitive("foo")), "but this works as well");

    // serialize like this
    String serialized = serialize(object);

    // parse it
    JsonElement json = jsonParser.parse(serialized);

    // and write it straight to some stream
    write(System.out, json, false);

    // or pretty print it like this
    System.out.println("\n" + serialize(json, true));
  }
}