David Wellman - 2010-03-26

J2J Tutorial
(est. 1 hour)

About J2J

JSON is very popular with JavaScript and front-end developers because of the native exchange with JavaScript and simplify of JSON based web services.  JSON is also growing in popularity as a data exchange and storage solution.  JSON avoids the complexity and verbose syntax of XML while still preserving rich data structures.

Today, most utilities and libraries that read or write JSON are often customized, proprietary salutations or are based on XML serialization making them difficult to maintain, support and enhance.  The goal of J2J is simply the generation of JSON from Java and mapping exiting JSON back again to native Java objects.

Key Elements:

The 5 key components of J2J are Java 3 annotations;  @SimpleJsonObject, @JsonElement, @JsonMapping and 2 Java classes JsonReader and JsonWriter.

@SimpleJsonObject is used to identify java classes that will be used to generate and map JSON.

@JsonElement is used to identify and custom name fields between Java and JSON.

@JsonMapping is used in advanced cases where custom logic or processing needs to be done before the final JSON is generated or when mapping JSON to complex objects.

JsonReader check that the JSON is valid and maps it to your java objects.

JsonWriter takes in annotated java objects and based generates valid JSON.

Example 1.1: Generating JSON from Java

The first step in creating JSON is to annotate the java classes that will be used to generate the data.  In this example identify that the Phone object with export JSON by adding the annotation @SimpleJsonObject to the the java class Phone. 

@SimpleJsonObject
public class Phone {
    private String type;
    private String number;
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number;
    }
}

At this point we use JsonWriter to generate JSON based on the existing getters.  Setting the property values:

Phone phone = new Phone();
phone.setType("fax");
phone.setNumber("646 555-4567");
String jsonString = new JsonWriter(user).toString();

If the user objects properties have been populated with data JsonWriter will generate a  the following JSON:

{"type":"fax","number":"646 555-4567"}

Example 1l2: Generating Named JSON elements:

The above example assumes that the property names are identical to the JSON names and that all the properties will be generated as part of the final JSON.  However when the desired JSON does not match the class structure identically the  @JsonElement can be used.  Adding  @JsonElement tell JsonWriter to only generate output from properties that have been annotated.  Removing an annotation will also remove it from the output.  The @JsonElement(name = "phone_number") will name the element other than the property name.

For example, the adding @JsonElement(name = "phone_number") the the getNumber() property:

@SimpleJsonObject
public class Phone {
    private String type;
    private String number;
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    @JsonElement(name = "phone_number") 
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number;
    }
}

will produce:

{"phone_number":"646 555-4567"}

Example 1.3: Complex Java Objects:

J2J has the ability to navigate down an the object graph picking up all sub object that have also been marked with the @SimpleJsonObject annotation.  The same rules for @JsonElement apply to the sub-objects.

For this example we will use 3 java object; User, Address and Phone.  The User object will contain Address as a sub-object and previous Phone object as an array of Phones

User Object

@SimpleJsonObject
public class User {
    private String firstName = null;
    private String lastName = null;
    private int age;
    private Address address = null;
    private List<Phone> phone = new ArrayList<Phone>();
           
    @JsonElement
    public String getFirstName() {
        return firstName;
    }
    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    @JsonElement
    public String getLastName() {
        return lastName;
    }
    public void setLastName(String lastName) {
        this.lastName = lastName;
    }
    @JsonElement
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    @JsonElement
    public Address getAddress() {
        return address;
    }
    public void setAddress(Address address) {
        this.address = address;
    }
    @JsonElement(name = "phoneNumber")
    public List<Phone> getPhone() {
        return phone;
    }
    public void addPhone(Phone phoneNumber) {
        this.phone.add(phoneNumber);
    }
}

Address Object

@SimpleJsonObject
public class Address {
    private String streetAddress;
    private String city;
    private String state;
    private String postalCode;
    public String getStreetAddress() {
        return streetAddress;
    }
    public void setStreetAddress(String streetAddress) {
        this.streetAddress = streetAddress;
    }
    public String getCity() {
        return city;
    }
    public void setCity(String city) {
        this.city = city;
    }
    public String getState() {
        return state;
    }
    public void setState(String state) {
        this.state = state;
    }
    public String getPostalCode() {
        return postalCode;
    }
    public void setPostalCode(String postalCode) {
        this.postalCode = postalCode;
    }
}

Phone Object

@SimpleJsonObject
public class Phone {
    private String type;
    private String number;
    @JsonElement
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    @JsonElement
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number;
    }
}

Next we need a simple Driver class to show how it all ties together.

public class Driver {
    public static void main(String[] args) {

        User user = new User();
        user.setFirstName("John");
        user.setLastName("Smith");
        user.setAge(25);

        Address address = new Address();
        address.setStreetAddress("21 2nd Street");
        address.setCity("New York");
        address.setState("NY");
        address.setPostalCode("10021");
        user.setAddress(address);

        Phone phone = new Phone();
        phone.setType("home");
        phone.setNumber("212 555-1234");
        user.addPhone(phone);
        phone = new Phone();
        phone.setType("fax");
        phone.setNumber("646 555-4567");
        user.addPhone(phone);

        String jsonString = new JsonWriter(user).toString(JsonWriter.PRETTY);

        System.out.println(jsonString);
    }
}

Running the Driver class will generate the following JSON output

{"address":{"state":"NY","streetAddress":"21 2nd Street","city":"New York","postalCode":10021},"firstName":"John","lastName":"Smith","age":25,"phoneNumber":[{"type":"home","number":"212 555-1234"},{"type":"fax","number":"646 555-4567"}]}

As default JsonWriter will generated compressed or minified JSON but JsonWriter can also create long from or pretty prettied JSON. Adding JsonWriter.PRETTY to the toString() method:

String jsonString = new JsonWriter(user).toString(JsonWriter.PRETTY);

will create the following pretty printed output:

{
   "address" : {
      "state" : "NY",
      "streetAddress" : "21 2nd Street",
      "city" : "New York",
      "postalCode" : 10021
   },
   "firstName" : "John",
   "lastName" : "Smith",
   "age" : 25,
   "phoneNumber" : [
      {
         "type" : "home",
         "number" : "212 555-1234"
      },
      {
         "type" : "fax",
         "number" : "646 555-4567"
      }
   ]
}

Example 1.4:  Advanced processing:
When the expected JSON does not map the Java class properties or if the class has complex data processing or other requirements you can add @JsonMapping  attribute to fine tune the resulting JSON.  The method that is annotated with @JsonMapping takes one parameter, (JsonMap map).

for example the following java class

@SimpleJsonObject
public class MethodProcess {
    @JsonMapping
    public void process(JsonMap map) {
        try {
            map.put("name", "John Doe");
            map.put("username", "jimbo");
            map.put("email", "john@doe.org");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

produces the following JSON:

{
   "name" : "John Doe",
   "useranme" : "jimbo",
   "email" : "john@doe.org"
}

Part 2: JsonReader
JsonReader is used similarly as JsonWriter, but takes in a JSON string and maps it to a Java objects. 

Example 2.1: mapping a JSON string to Java

given the following JSON string

{"name":"Bob Doe","username":"bobby","email":"bob@doe.org"}

JsonReader take the class definition and the JSON string as parameters mapping the data to a User object with the following command:

User user = (User) new JsonReader(User.class, son).toObject();

Example 2.2: Complex JSON
When the JSON represent a complex data structure it can be mapped to a similar Java object structure using JsonReader.  To tell JsonReader how to map data to Java object you simply need to add path to the @SimpleJsonObject annotation like @SimpleJsonObject(path = "//…").

The sample JSON  we will use is:

{
    "address" : {
        "address" : "3912 West end drive",
        "state" : "Hong Kong",
        "city" : "Happy Valley"
    },
    "screen_name" : "John Johnson",
        "id" : 808,
        "user_id" : "dork1",
        "email_address" : "john@nowhere.com",
        "phone_numbers" : [
        {
            "phone_type" : "home",
            "phone_number" : "(555) 321-1234"
        },
        {
            "phone_type" : "cell",
            "phone_number" : "(555) 888-1234"
        },
        {
            "phone_type" : "work",
            "phone_number" : "(555) 999-1234"
        }
    ]
}

The Address object maps to //user/address and the PhoneNumber object maps to //user/phone_numbers.

@SimpleJsonObject(path = "//user/phone_numbers")
public class PhoneNumber {
    private String type;
    private String number;

    public PhoneNumber() {
    }

    public PhoneNumber(String type, String number) {
        this.type = type;
        this.number = number;
    }
    @JsonElement(name = "phone_type")
    public String getType() {
        return type;
    }
    public void setType(String type) {
        this.type = type;
    }
    @JsonElement(name = "phone_number")
    public String getNumber() {
        return number;
    }
    public void setNumber(String number) {
        this.number = number;
    }   
}

Next we need to tell JsonWriter where those object are found in our project.  This is done using JsonClassScanner. takes in one parameter, a string defining the root package.  We are now ready to map the JSON to the Java object:

    // Read the entire contents of sample.txt
    String json = FileUtils.readFileToString(file);
    // For shake of this example we show the file content here.
    JsonClassScanner scanner = new JsonClassScanner("com.zenworld");

    // map the object.
    Object actual = new JsonReader(User.class, scanner, json).toObject();

Conclusion
Please explore the examples included with J2J to become more familiar advanced features of the library.  Let us know your successes and uses of JsonWriter and JsonReader.  We also welcome any suggestions for improvement, bug or change requests that you may have.

Enjoy.

References
http://www.json.org
http://en.wikipedia.org/wiki/JSON
http://en.wikipedia.org/wiki/Comparison_of_data_serialization_formats
http://www.ietf.org/rfc/rfc4627.txt