My previous postings were concerned with developing Restful services delivering JSON payloads. Using a combination of JAX-RS and JAXB I was able to rapidly develop services and clients for my REST services.

In my current project I’m working with a non-restful service that manipulates JSON payloads, so I need to parse and produce JSON strings without the assistance of JAX-RS frameworks. My challenge is to make something like this happen:

    public String doSomeWork(String theJson) {

        InterestingObject payload = parseJson( theJson );

        // now I have a Java object I can work …

        ResultObject answer =  worker.process(payload);

        return formatJson(answer);  // need to produce JSON format

    }

I’ve discovered that the open source Jackson parser lets me do this, so  in this article I’m going to describe some of the Jackson features I’ve used.

Set Up the Java Project

I’m using Rational Software Architect 7.5.3, and I create a simple Java project using the default Java environment,  JSE 1.6. I would expect any equivalent Eclipse-based environment to be equally suitable.

I download two Jackson components: core and mapper from the Jackson Download site. Jackon supports a number of binding models, I want to use the”Data Binding” approach, which maps Java Beans to and from JSON and for that I need the mapper jar in addition to the Jackson core functionality.

download

I am using the Apache-licenced versions of these JARs. I drag the downloaded JARs to my Java project and then add them to my classpath.

Select the project, rightClick->Properties, Java Build Path, Add JARs and in the JAR Selection dialogue select both JARs.

BuildPath

The project referenced libraries now shows the use of the two JARs. Note that by including the jars here, the project becomes a self contained entity that can be shared via a source control system such as CVS. In the long term we should probably avoid duplication and instead put the libraries into their own project.

ReferencedLibs 

The Data

The real data I’m dealing with is quite technical, relating to the IIF framework, telemetry on oil rigs and so on. So here’s a small, fictitious example with some similar features. It’s represents the response to a query of a music catalogue for works by particular artists.

{
      "success": true,
      "artists": [
         {
            "name": "Ashley Hutchings",
            "albumns": [
               {
                  "title" : "Copper, Russet and Gold",               
                  "properties": [
                     { "name": "artist",
                       "value" : "Ashley Hutchings, Ken Nicol"
                     },
                     { "name": "id",
                       "value" : "PRKCD109"
                     }                    
                  ]
               }, 
               {       
                  "title" : "Twanging and a Tradding",

      … etc.

The response comprises a success indicator and then an array of the artists that were found. Each artist contains an array of albums and they in turn have an array of name/value pair properties. That property set will present a little challenge later, but first let’s deal with the easier pieces.

The example JSON string I stash in a file in my Java project. My code will read it from there, though of course in real life the data is being delivered from remote services.

The complete test file:  Albumn List

The Application, untyped parsing

Jackson a very simple way to parse this JSON, it will produce a HashMap containing the parsed data with very few lines of code.

I present the whole code here. Towards the end there are a few non obvious lines, so I’ll explain them in more detail,

package djna.jackson.eg;

import java.io.File;
import java.io.IOException;
import java.util.HashMap;

import org.codehaus.jackson.JsonFactory;
import org.codehaus.jackson.map.ObjectMapper;
import org.codehaus.jackson.type.TypeReference;

public class JsonExample {
    public static void main(String argv[]) {
        try {
            JsonExample jsonExample = new JsonExample();
            jsonExample.testJackson();
        } catch (Exception e){
            System.out.println("Exception " + e);
        }       
    }
    public void testJackson() throws IOException {       
        JsonFactory factory = new JsonFactory();
        ObjectMapper mapper = new ObjectMapper(factory);
        File from = new File("albumnList.txt");
        TypeReference<HashMap<String,Object>> typeRef
              = new TypeReference<
                     HashMap<String,Object>
                   >() {};
        HashMap<String,Object> o
             = mapper.readValue(from, typeRef);
        System.out.println("Got " + o);
    }   

}

The package, import and main() code are pretty much standard Java, I want to focus on the testJackson() method. Its purpose is to read the file containing a JSON string into HashMap representation and print out the result. I’m using a file here but there are similar readValue() variants for reading from other sources such as InputStreams.

The first thing we see is the creation of an instance of a class that is quite widely used in Jackson, the ObjectMapper.

       JsonFactory factory = new JsonFactory();
     ObjectMapper mapper = new ObjectMapper(factory);

A common pattern is to create a single ObjectMapper and reuse it but here I’m creating an instance for use just in this example method. The next line creates a File instance for our source of data. In my eclipse-based environment the default working directory will be my project, so the file is immediately accessible.

    File from = new File("albumnList.txt");

Now for the tricker bits of code. This line is where the parsing happens:

    HashMap<String,Object> o
             = mapper.readValue(from, typeRef);   

The general pattern here is that we ask the mapper to read from the File and produce an object, the result is assigned to a HashMap<String, Object>, so mapper.readValue() must produce an object of that type. How does it know what type? from the second parameter – typeRef. This is where the Java Generic capabilities impose a wrinkle. In concept we pass a class to readValue() to say “make me one of these”. But Java Generics don’t make that easy (read up on Type Erasure if you’re interested) so Jackson provides a a TypeReference class to allow us to describe the Generic we want. Hence this line of code:

    TypeReference<HashMap<String,Object>> typeRef
              = new TypeReference<
                     HashMap<String,Object>
                   >() {};

Note that we are creating an instance of an anonymous class, which is sufficient to pass the necessary type information.

Now when we execute the Java Application, the print

System.out.println("Got " + o);

Will print the HashMap and we get output like this:

Got {success=true, artists=[{name=Ashley Hutchings, albumns=[{title=Copper, Russet and Gold

This shows that the JSON string has been parsed, but on seeing that default HashMap.toString() output I wonder how readily I could instead output a JSON string. Can we serialise the data we read back to JSON? It transpires that this is pretty easy too, but does lead to an understanding of how to configure some Jackson behaviours. So, a quick diversion via Jackson serialisation

Serialising to JSON

The code to produce a JSON string is pleasingly simple:     

        mapper.writeValue(System.out, o);
        System.out.println("\nComplete.");

and this produces the following output:

    {"success":true,"artists":[{"name":"Ashley Hutchings  … etc

with one small surprise, that “Complete” line is not written. This is because by default  the Jackson output streams are closed when serialization is complete, and that’s probably not what we want for System.out. We can alter this behaviour using the mapper’s configure method

     mapper.configure(
                JsonGenerator.Feature.AUTO_CLOSE_TARGET,
                false);   

Similar techniques can be used to configure other Jackson behaviours. As an example, suppose we prefer the JSON string to be formatted:

    mapper.getSerializationConfig().set(
        SerializationConfig.Feature.INDENT_OUTPUT,
        true);

This produces output like this

      (beginning omitted) … "title" : "Original Owners",
      "properties" : [ {
        "name" : "artist",
        "value" : "Michael Chapman, Rick Kemp"
      }, {
        "name" : "id",
        "value" : "RR002"
        } ]
      } ]
    } ]
  }
  Complete.

Jackson provides several configuration enums  for configuring different aspects, each enum is called “Feature” and is declared in the appropriate class. To avoid import collisions import the class, not the enum:

   import org.codehaus.jackson.JsonGenerator;
   import org.codehaus.jackson.map.SerializationConfig;

So Far

That gets us started with Jackson, but we haven’t got a nice, typed representation of the data. More on that next time …

Advertisements