Flexjson

Support Flexjson by Donating

JavaScript Object Notation (aka JSON) is a very popular alternative to XML for transmitting data to the web browser. Flexjson is a lightweight library for serializing and deserializing Java objects into and from JSON. What's different about Flexjson is it's control over what gets serialized allowing both deep and shallow copies of objects. Most JSON serializers mimic object serialization libraries and try to serialize the entire object graph from the object being turned into JSON. This causes problems when you want a connected object model in your server, but you can't send that object model to the client because the serialization library will try to send the entire object graph. This makes it very hard to create object oriented models and serialize pieces of that model to the client without sending everything. Other JSON libraries require you to create a lot of boiler plate code to translate your object representation into a JSON object. In this regard I shouldn't have to explain myself, but to keep it short let's me just say I hate boiler plate translation code! Flexjson tries to solve both of these problems by offering a higher level API or dare I say DSL for specifying your intent.

In order to explore Flexjson we will be using the following model. Let's say we're building a PIM or contact management application. Here are some classes we might see in an application like this along with the relationships between them.

UML Diagram of Person, Address, Zipcode, and Phone classes.

In the above diagram you can see Person has many instances of Phone, and has many Addresses. While this is a trival object model it will help us demostrate the ideas behind Flexjson.

Serialization

The Basics

Flexjson takes a different approach allowing you to control the depth to which it will serialize. It's very similiar in concept to lazy loading in Hibernate which allows you to have a connected object model, but control what objects are loaded out of your database for performance. Let's look at a simple example first to get a feel for how the library works. Say we are serializing an instance of Person. We might do the following:

public String doSomething( Object arg1, ... ) { Person p = ...load a person...; JSONSerializer serializer = new JSONSerializer(); return serializer.serialize( p ); }

The above would produce the following output:

{ "class": "Person", "name": "William Shakespeare", "birthday": -12802392000000, "nickname": "Bill" }

Working With Includes

Looks pretty much like you'd expect. However, our Person object contains more fields than name, birthday, and nickname so what happened to phoneNumbers, and addresses? By default Flexjson serializes the immediate fields of that object. It's just a shallow representation of the object. All collections are not serialized by default. Or said another way any one to many, and many to many relationships are not serizialized by default. Object references are serialized by default. That being many to one and one to one relationships will be serialized by default. This is consistent with other libraries like hibernate and JPA which will eagerly load these relationships by default. But, let's say we wanted to include the phoneNumbers field in our output then we could do the following:

public String doSomething( Object arg1, ... ) { Person p = ...load a person...; return new JSONSerializer().include("phoneNumbers").serialize(p); }

In this example, we're telling the serializer to include the following fields from the target object. Collections are serialized in full including the shallow copy of the objects they contain. So in our example, Person has a List field called phoneNumbers, and that List contains Phone instances. This means Flexjson will send a shallow copy of Person, the List of phoneNumbers, and a shallow copy of each Phone instance within the List. So the output might look like:

{ "class": "Person", "name": "William Shakespeare", "birthday": -12802392000000, "nickname": "Bill" "phoneNumbers": [ { "class": "Phone", "name": "cell", "number": "555-123-4567" }, { "class": "Phone", "name": "home", "number": "555-987-6543" }, { "class": "Phone", "name": "work", "number": "555-678-3542" } ] }

Getting the hang of it? It's pretty simple. If you wanted to send both phoneNumbers and addresses you can call include method twice, or you can include two parameters to the include method using Java's new vararg feature. Personally I prefer the later since I think it makes it shorter and easier to read. But, it's your choice.

public String doSomething( Object arg1, ... ) { Person p = ...load a person...; return new JSONSerializer().include("phoneNumbers", "addresses").serialize(p); }

So in this case we'll only get the street, city, and state, but not the zipcode because that is an object reference. It's an easy fix using the dot notation. Here is an example to include the zipcode with each Address instance.

public String doSomething( Object arg1, ... ) { Person p = ...load a person...; return new JSONSerializer().include("phoneNumbers", "addresses.zipcode").serialize(p); }

Flexjson is smart enough to know you mean the object contained with this collection and not the collection itself. It's also smart enough to know that the addresses reference has to be included in order to include zipcode so you don't have to specify it twice. You can use the dot notation to trace over your object graph very easily.

More on Includes

There is an option that allows you to specify an outer object for collections. Using the rootName() method you can specify the property of that outer object. There are some javascript libraries like EXTJS (previously known as YUI-EXT) that require this for their JSON data models. However, I haven't found any JSON libraries that offer this type of serialization. Here is an example:

The rootName() method is new as of Flexjson 2.0. Prior versions used an alternate version of serialize(). The reason for this new method allowed for more versions of serialize without requiring too many overload method combinations.
public String getPeople( Object arg1, ... ) { List people = ...load a person...; return new JSONSerializer().include("phoneNumbers").rootName("people").serialize(people); }

The resulting JSON would look like the following:

{ "people" : [ { "class": "Person", "name": "Warren Buffet", "birthday": -1241467200000, "nickname": "Oracle of Omaha", "phoneNumbers" : [ ... ], }, { "class": "Person", "name": "Steven Jobs", "birthday": -468702000000, "nickname": "Steve", "phoneNumbers" : [ ... ], } ] }

Working With Excludes

Not only can you include, but you can specify it in terms of exclude as well. The exclude() method allows you to exclude certain fields from serilization. This can come in handy if you have special fields that you don't want sent to the client like passwords, or secret data that should remain on the server.

public String doSomething( Object arg1, ... ) { User u = ...load a user...; return new JSONSerializer().exclude("password").include("hobbies").serialize(p); }

Using dot notation with exludes has a subtle difference in it's use when compared with includes. If you exclude a nested field it implies that the rest of the parent object is included. So if I say exclude("head.rightEye.retinalScan"). The retinalScan member of rightEye field will be excluded, but both rightEye and head field will be included. The reason is in order to exclude retinalScan field you have to include the rightEye member. If you didn't then it doesn't change anything because retinalScan wasn't going to be included in the first place. Said another way it's only the last field that is excluded all other parent fields are included.

Excluding Using Annotations

Always having to do your excludes in this manner can be cumbersome especially if you always intend something to be excluded or included. Flexjson provides a way to express this using annotations. The JSON annotation can be used to mark a field in the object as included by default. Annotations can be placed on the methods or fields. A good example of this might be the Address object that contains a reference to Zipcode object. Since Zipcode is an integral part of every Address (in the US) then it might make more sense for us to default this with the annotation. So in our Address object we could do the following:

public class Address { private String name; private String street; private String city; private String state; private Zipcode zipcode; @JSON public Zipcode getZipcode() { } }

Annotations can improve your security so that fields that shouldn't be sent over the wire ever can be fixed in one place. That way you keep your code more DRY, and that prevents accidental security flaws. Consider if we store the a hashed version of a password in our User object.

public class User { private String login; private String hashedPassword; private Date lastLogin; @JSON(include=false) public String getHashedPassword() { } }

Code Gone Wildcard

In 1.5 you can now specify wildcards in your include() and exclude() method calls. This makes it easy to include or exclude several fields very easily. It's very common for people to want to remove the class attribute written to the stream. With wildcards it becomes very easy to accomplish this. For example:

public String doSomething( Object arg1, ... ) { User u = ...load a user...; return new JSONSerializer().exclude("*.class").serialize(p); }

Any path ending with class will now be excluded from the output. Wildcards match any depth you specify. So it doesn't matter how deep your object path has gotten. An exclude of *.class will match to any path depth. So if flexjson is serializing the field with path of "foo.bar.class" the "*" in "*.class" will match foo.bar.

Wildcards do not expand the depth to which you are serializing in most cases. They only operate in the depth you have specified. That means if you specify *.list, paths like foo.list would be serialized, but foo.bar.list would not. If you wanted foo.bar.list you would need have an include for foo.bar from another statement. Same goes for things like *.class. Every object has a class member because of Object.getClass(). However, *.class won't extend the depth of your serialization. There are cases where serialization depth will be expanded. You have to be careful when doing specifying wildcards with recursive structures. If you had a tree structure and did an include on *.children it will expand the depth of your serialization all the way. The other case where wildcards will expand your serialization is "*". That is the same as doing a deep serialization so be careful using it.

Order of Evaluation

Order of evaluation has changed as of 1.5

Now that we've discussed includes and excludes let's talk about order of evaluation. What happens if I include a field then I exclude that same field later? What will Flexjson do? The answer is includes and excludes are evaluated in order they were added. This is important with the introduction of wildcards. Say I want to serialize just two fields of an object. I can do the following:

public String doSomething( Object arg1, ... ) { User u = ...load a user...; return new JSONSerializer().include("username").exclude("*").serialize(p); }

The above code would serialize just the username field from the user object, but it would exclude all other fields. If I reversed the order of the include and exclude calls it would exclude all of the fields, and the username field wouldn't be included? Why is that? That's because internally flexjson would have a list of expressions. Think of it like a list where the order is the following:

[ "*", "username" ];

Flexjson would visit each field in the User object, and evaluate it against all of the rules in the list. It starts the head of the list and stops once it finds the first match. In this case "*" would match all fields of User object, and it would exclude it thus never reaching the "username" entry.

Direct Serialization To a Stream

For simplicity Flexjson allows you to work with String objects, but but it's more efficient to work directly with the stream we want to send the json data over. You can supply a java.io.Writer as the destination for the json output. Now we can go directly to the stream bypassing the conversion to String. Here is a simple example:

new JSONSerializer().serialize(myObject, response.getPrintWriter());
This method returns void so there is no need to handle the return type.

Deep Serialization

In 1.2 there was a new feature added to perform a deep serialization of the object using the deepSerialize() method. As with serialize() method deepSerialize() method will use includes, excludes, and annotations to figure out what you want sent. However, includes are generally redundant except in the case where you want override an excluding annotation on a field. Deep serialization will not serialize cycles in your graph so if you have a bi directional relationship say parent has a child and child has a parent. The back reference from child to parent won't be included in the JSON output. This is the same for shallow serialization as well. Here is a quick example:

public String doSomething( Object arg1, ... ) { Person p = ...load a person...; return new JSONSerializer().deepSerialize(p); // send the entire graph starting at person }

Deep serialization should be used sparingly, and reserved for only cases where you really understand what is being serialized. Deep serialization can send a lot more data that you anticipated if you use it frivolously.

Controlling JSON naming with @JSON

When an object is serialized the names of the properties on JSON objects are determined from the name of the Java property. This means the names of the getter methods or the field names are used. However, sometimes the JSON output doesn't match the names used by the Java object. You can control this using the @JSON annotation. For example:

public class Species { @JSON(jsonName = "genus") private String type; private String name; @JSON(jsonName="species") public String getName() { return name; } }

Notice the annotation can be placed on the field or getter method which would produce the following JSON output:

{ "genus": "Agaricus", "species": "Agaricus campestris" }

Defining a @JSON.jsonName is used in both serialization and deserialization.

Transformers More Than Meets the Eye

Transformers are central to how Flexjson works in 2.0.

Flexjson 2.0 has been completely rewritten to make Transformers central to how processing Java into JSON works. With this core restructuring, Transformers have much more flexibility and power.

What is a transformer?

Transformers are classes that are used to handle the actual transformation of Java objects into JSON.

How to configure a transformer

There are 3 ways you can configure a transformer:

Configuring a Transformer Using a String to Identify a Property

This will only apply the transformer to the specified property/properties identified by the String varargs that are provided after the first parameter on the tranform method.

JSONSerializer serializer = new JSONSerializer() .transform(new BasicDateTransformer(), "person.birthday");

or you can specify as many more properties to be handled by this Transformer as you want.

JSONSerializer serializer = new JSONSerializer() .transform(new BasicDateTransformer(), "person.birthday", "person.hireDate");

Configuring a Transformer Using A Class Identifier

Using a class will apply the transformer to any object of that type.

JSONSerializer serializer = new JSONSerializer() .transform(new BasicDateTransformer(), Date.class);

Using String Identifiers with Class Identifiers

It should be noted that there is a precedence to the use of combined transformer identifiers. The String property identifiers will override the class identifiers.

JSONSerializer serializer = new JSONSerializer() .transform(new BasicDateTransformer(), Date.class) .transform(new DateTransformer("MM/dd/yyyy"), "person.birthday");

The property identified by "person.birthday" would use the DateTransformer only. Any other Date objects would use the BasicDateTransformer.

Using @JSON annotation to configure Transformer

Add the @JSON annotation to a field or property method.

public class SomeObject { @JSON( transformer = SomeFieldTransfomer.class ) private SomeField someField; }

Overriding Flexjson default Transformers

It is possible to override the default Transformers Flexjson uses. If you wanted to override the default StringTransformer that Flexjson uses for Strings you could write configure in the following manner:

JSONSerializer serializer = new JSONSerializer() .transform(new MyCustomStringTransformer(), String.class);

Basic Transformer

Flexjson comes with several basic Transformers that are used to meet the majority of your Java-to-JSON needs.
Flexjson comes with the following default Transformers:

How Transformers Work

In order to understand the role that Transformers play it is important for us to understand what the JSONSerializer and the JSONContext do as well.

JSONSerializer

The JSONSerializer works as a configuration object. Once serialize/deepSerialize is called on the JSONSerializer it retrieves a ThreadLocal version of a JSONContext and configures it for use specific to the calling Thread.

JSONContext

The JSONContext facilitates several aspects of the transformation process and keeps the Transformers as simple as possible.

The JSONContext performs the following major functions:

  1. Initializes the transformation process.
  2. Handles the traversal of the Java Objects being translated.
  3. Calls the correct Transformer for the object being translated.
  4. Hosts and outputs the resulting JSON
  5. Maintains state and information about the object(s) being transformed.
  6. Provides common utility methods for tailoring the JSON output.

Transformers

In the end the Transformer is responsible for deciding how to translate the passed in object to JSON, making the appropriate calls on the JSONContext object to output the JSON, and/or passing the object along the transformation process.

Simple Transformer

The StringTransformer provides a simple example of a Transformer.

package flexjson.transformer; public class StringTransformer extends AbstractTransformer { public void transform(Object object) { getContext().writeQuoted((String) object); } }

In the StringTransformer the Object passed in is a String which gets quoted by calling a method (getContext().writeQuoted()) on the JSONContext. The JSONContext takes care of all the string escaping that needs to occur. Pretty simple eh?

As a side note you may notice that the Transformer extends AbstractTransformer. It is recommended that custom transformers extend the AbstractTransformer although it is not required. Feel free to take a look in the AbstractTransformer to see what it does. There isn't much to it.

Transforming a Collection

In this example we'll take a look at the IterableTransformer which transforms collections.

public class IterableTransformer extends AbstractTransformer { public void transform(Object object) { Iterable iterable = (Iterable) object; TypeContext typeContext = getContext().writeOpenArray(); for (Object item : iterable) { if (!typeContext.isFirst()) getContext().writeComma(); getContext().transform(item); } getContext().writeCloseArray(); } }

You will notice that this transformer makes use of a TypeContext. The TypeContext is just a way to keep track of whether the current context is an ARRAY (Collection or Array) or a BASIC (Any Object other than Collection or Array) object. Additional information on the current object context is also held in the TypeContext. Behind the scenes in the JSONContext.writeOpenArray() method the JSONContext creates the TypeContext as BasicType.ARRAY, pushes it on the typeContextStack, and then returns the type context from the writeOpenArray() method. This context is then made available to all downstream transformations via the getContext().peekTypeContext() method.

Custom Transformer using Inline functionality

This example of the FlatDateTransformer (found in the Flexjson unit tests) is more complex than the last. What it illustrates is the flexibility that Transformers have when needing to meet various requirements.

Let's take some time to understand what the FlatDateTransformer does and then we'll discuss how it does it. In this particular case a java.util.Date is transformed into 3 separate properties on a JSON object. If we were to pass a Date object into this transformer the resulting JSON would look like:

{ "month":11, "day":13, "year":2007 }

This transformer "inlines" the Date properties with the other properties of the current object. For example, if I had an object named Task with a Date property (named doOn) along side name and description this Transformer would render the date inline with the other properties when the JSON is generated.

public class Task { private String name; private String description; private Date doOn; // setter/getters left out for brevity }

Would result in:

{ "name":"some name", "description":"some description", "doOnMonth":11, "doOnDay":13, "doOnYear":2007 }

As you can see the doOnMonth, doOnDay, and doOnYear are next to the other properties rather than in a nested JSON object. You may also notice that each one was automatically prefixed with the Date properties' name of doOn. This brings me to the prefix property that can be passed into the FlatDateTransformer. The prefix allows us to alter the generated property name to whatever we like when inlining. Lets look at our Task example again but this time we'll provide a prefix.

public class Task { private String name; private String description; private Date doOn; // setter/getters left out for brevity }

If we did not specify a prefix in the JSONSerializer it automatically picks up the name of the property being transformed in Java and uses it to prefix the month, day, and year JSON properties. However, if we were to specify our own in the JSONSerializer it would look different.

JSONSerializer serializer = new JSONSerializer() .transform(new FlatDateTransformer("shazam"), "doOn"); Task task = new Task(); // set properties serializer.deepSerialize(task);

would result in:

{ "name":"some name", "description":"some description", "shazamMonth":11, "shazamDay":13, "shazamYear":2007 }

In summary the FlatDateTransformer is a clear example on how to make use of the inline functionality to take greater control over the generated JSON. In order to accomplish this the FlatDateTransformer extends the AbstractTransformer and overrides the isInline method.

@Override public Boolean isInline() { return Boolean.TRUE; }

If isInline() is overridden and returns Boolean.TRUE it tells the JSONContext that we will be handling the writing of our own property name. This allows us full control over the generated JSON instead of relying on the standard reflection to lookup the property name or key name and use it as the JSON property name. As a result we need to handle every aspect of the JSON generation for this object now.

Once the transform() method is called on the FlatDateTransformer it first checks to see if a TypeContext exists. If a type context exists it checks to see if a property name exists on the TypeContext and sets it. Afterwards it checks to see if a prefix has been specified. If a prefix does not exist it is replaced with the propertyName. Otherwise prefix is left as is.

TypeContext typeContext = getContext().peekTypeContext(); String propertyName = typeContext != null ? typeContext.getPropertyName() : ""; if(prefix.trim().equals("")) prefix = propertyName;

Afterwards the TypeContext is checked. If the JSONContext does not yet contain a JSONContext or is not currently in a BasicType that is equal to BasicType.OBJECT then we will need to start writing a new JSON Object. Hence we call the writeOpenObject() method on the JSONContext. This creates a TypeContext that has a BasicType of OBJECT inside the writeOpenObject() method and pushes it onto the context stack in the JSONContext. If a TypeContext with a BasicType of OBJECT already exists on the stack then the properties are output as part of the currently generating JSON object.

if (typeContext == null || typeContext.getBasicType() != BasicType.OBJECT) { typeContext = getContext().writeOpenObject(); setContext = true; }

After this we just need to generate the JSON output.

Date date = (Date) o; Calendar c = Calendar.getInstance(); c.setTime(date); if (!typeContext.isFirst()) getContext().writeComma(); getContext().writeName(fieldName("Month")); getContext().transform(c.get(Calendar.MONTH)); getContext().writeComma(); getContext().writeName(fieldName("Day")); getContext().transform(c.get(Calendar.DAY_OF_MONTH)); getContext().writeComma(); getContext().writeName(fieldName("Year")); getContext().transform(c.get(Calendar.YEAR));

All of this is fairly self explanatory. But, I'll draw special attention to the use of typeContext.isFirst(). This is used to check if the currently generating property is the first one to be created for this JSON object. If it is then we avoid writing the comma. If we did not perform the isFirst check we would wind up with JSON that looks like this:

{,"month":11,"day":13,"year":2007} ^ notice the invalid comma at the beginning of the JSON

Finally, we close our JSON Object by checking if we created the TypeContext in this transformer.

if (setContext) { getContext().writeCloseObject(); }

Here is the full source for the FlatDateTransformer:

public class FlatDateTransformer extends AbstractTransformer { String prefix = ""; public FlatDateTransformer(String prefix) { this.prefix = prefix; } public void transform(Object o) { boolean setContext = false; TypeContext typeContext = getContext().peekTypeContext(); String propertyName = typeContext != null ? typeContext.getPropertyName() : ""; if(prefix.trim().equals("")) prefix = propertyName; if (typeContext == null || typeContext.getBasicType() != BasicType.OBJECT) { typeContext = getContext().writeOpenObject(); setContext = true; } Date date = (Date) o; Calendar c = Calendar.getInstance(); c.setTime(date); if (!typeContext.isFirst()) getContext().writeComma(); getContext().writeName(fieldName("Month")); getContext().transform(c.get(Calendar.MONTH)); getContext().writeComma(); getContext().writeName(fieldName("Day")); getContext().transform(c.get(Calendar.DAY_OF_MONTH)); getContext().writeComma(); getContext().writeName(fieldName("Year")); getContext().transform(c.get(Calendar.YEAR)); if (setContext) { getContext().writeCloseObject(); } } private String fieldName(String suffix) { if( prefix.trim().equals("")) { return suffix.toLowerCase(); } else { return prefix + suffix; } } @Override public Boolean isInline() { return Boolean.TRUE; } }

Thread Safety and Reuse

Finally, JSONSerializer instances can be reused to serialize many objects of the same type. Once you instantiate the object you can reuse it across multiple threads so long as you don't call include or exclude methods. Typically this isn't an issue since you might follow this pattern of instantiation:

Thread safety is guaranteed with the default Transformers, but custom Transformers you write can have an impact on thread safety of the JSONSerializer. Take care not to use internal state inappropriately, or use libraries that aren't thread safe in your Transformers.
public class PersonController { JSONSerializer personSerializer; public PersonController() { personSerializer = new JSONSerializer().include("addresses.zipcdoe"); } public String listPerson() { Person p = ....; return personSerializer.serialize( p ); } public String editPerson() { Person p = ....; return personSerializer.serialize( p ); } }

Deserialization

Deserialization is the process of taking JSON text binding those values into Objects. This process can be quite complex as JSON text contains no typing information. Mapping JSON data types onto static Java objects is tricky. Flexjson supports doing this through the JSONDeserializer class.

JSONDeserializer takes as input a json string and produces a static typed object graph from that json representation. By default it uses the class property in the json data in order to map the untyped generic json data into a specific Java type. This works quite well for JSON generated from flexjson. Let's just look at the simplest case using the class attributes in your json, and how JSONSerializer and JSONDeserializer nicely pair together out of the box.

Say we have a simple object like Hero (see the superhero package under the test and mock). To create a json representation of Hero we'd do the following:

Hero harveyBirdman = new Hero("Harvey Birdman", new SecretIdentity("Attorney At Law"), new SecretLair("Sebben & Sebben") ); String jsonHarvey = new JSONSerialize().serialize(hero);

Now to reconstitute Harvey to fight for the law we'd use JSONDeserializer like so:

Hero hero = new JSONDeserializer<Hero>().deserialize( jsonHarvey );

Deserialize Without the Training Wheels

Pretty easy when all the type information is included with the JSON data. Now let's look at the more difficult case of how we might reconstitute something that is missing type info.

Let's exclude the class attribute from our json like so:

String jsonHarvey = new JSONSerialize().exclude("*.class").serialize(hero);

We need to replace the type information we just dropped when we instantiate the deserializer. To do that we'll pass the class we want to use into to flexjson.JSONDeserializer.deserialize(String, Class) method like so:

Hero hero = new JSONDeserializer<Hero>().deserialize( jsonHarvey, Hero.class );

Prior to 2.0 you have to use the following to specify the root level object by calling use() method passing null for the path.

Hero hero = new JSONDeserializer<Hero>().use( null, Hero.class ).deserialize( jsonHarvey );

In both cases, what is happening here is we've registered the Hero class onto a path in the json object graph. We use null here to mean the root of the json object. If we were naming our root we'd include that name as the path to our root in the JSON. The flexjson.JSONDeserializer.use(String, Class) method uses the object graph path to attach certain classes to those locations. So, when the deserializer is deserializing it knows where it is in the object graph, and uses that graph path to look up the java class it should use to gather type information from.

Notice that all we registered was Hero class how did it know to instantiate SecretLair and SecretIdentity too? Flexjson uses the target type (in this case Hero) to figure out the other types by inspecting that class. Meaning that we didn't have to tell it about SecretLair or SecretIdentity because it can figure that out by inspecting the properties in the Hero class. Pretty cool.

Polymorphism, Subclasses, and Interfaces

Where we run into trouble is when we starting working with interfaces, abstract classes, and subclasses. Our friend polymorphism has rained on our parade. Why? Let's say our target type has a member with a type of java.util.List. Is that an ArrayList, LinkedList, or something else? By looking at the target type alone we don't know. The next section we're going to discuss how to handle these tricky problems.

Before we showed how the flexjson.JSONDeserializer.use(String, Class) method would allow us to plug in a single class for a given path. That might work when you know exactly which class you want to instantiate, but when the class type depends on external factors we really need a way to specify several possibilities. That's where the second version of {@link flexjson.JSONDeserializer#use(String, ClassLocator)} comes into play. A ClassLocator allows you to define a strategy for finding which java Class you want to attach at a particular object path dynamically.

flexjson.JSONDeserializer.use(String, ClassLocator) has access to the intermediate form of the object as a Map. Given the Map at the object path the ClassLocator figures out which Class Flexjson will bind the parameters into that object.

Let's take a look at how this can be done using our Hero class. All Heros have a list of super powers. These super powers are things like X Ray Vision, Heat Vision, Flight, etc. Each super power is represented by a subclass of SuperPower. If we serialize a Hero without class information embedded we'll need a way to figure out which subclass to instantiate when we deserialize. In this example I'm going to use a Transformer during serialization to strip off the package on the class attribute. This is not normally how you might do this, but I'm being lazy and doing a quick example.

String json = new JSONSerializer() .include("powers.class") .transform( new SimpleTransformer(), "powers.class") .exclude("*.class") .serialize( superhero ); Hero hero = new JSONDeserializer<Hero>() .use("powers.class", new PackageClassLocator()) .deserialize( json );

That's a very contrived example because all I'm doing is stripping off the package and then adding back that missing package in my ClassLocator. A more typical case would be when there is some typing information included in your JSON objects that can be used as a discriminator between the various types of Classes you need. The good news is flexjson provides a ClassLocator out of the box that provides this so you don't have to rewrite this.

TypeLocator class uses a field in the JSON to select a particular class you want to use. Here is a quick example of this. Say for example, in our JSON we have a field called "type" that is either a square, circle, or triangle. Based on the value of that field we want to use Square, Circle, or Triangle classes to be instantiated.

String json = new JSONSerializer().serialize( shapeList, "shapes" ); ShapeList shapes = new JSONDeserializer<ShapeList>() .use("shapes", new TypeLocator<String>("type") .add("square", Square.class) ) .add("circle", Circle.class) ) .add("triangle", Triangle.class) ) ) .deserialize( json )

In this example the deserialization definition is a little longer, but not bad. We're creating a TypeLocator instance saying the field that controls the typing is called "type", and when the value is "square" we want to use Square class, when it's "circle" use Circle class, and so on. TypeLocator is a generic class that lets you specify the datatype of the discriminator field. In this case we're using strings, but you could use integers just the same.

In this example I'm serializing a list of shapes, and I'm naming my list "shapes" which means my root isn't null anymore. This is just to show how paths work, and doesn't really have any relation to the use of TypeLocator.

Collections, Maps, and Generics

Collections and Maps are somewhat special cases of interfaces because they contain objects within them that aren't accessible through setter and getter methods. Normally, if you use generics and specify a concrete class as the generic class contained then you won't have anything else to do. However, mix in an interface, abstract class, or root level object containing Generics and you'll have tell the deserializer the types contained within.

Starting with 1.9 you can specify both the concrete class used for the collection class as well as the contained concrete class. By appending "values" to the path of any collection you can configure the concrete class of the contained class.

For example, say a Group object contains a Collection of Groupable instances where Groupable is an interface. We'll need to specify what concrete class to use for the Groupable. For simplicity in this example we'll assume all Groupable instances can be represented by a single concrete class. If the path to the Group's instance variable members of type Collection<Groupable>. So we might do the following:

new JSONDeserializer().use( "group.members.values", ConcreteGroupable.class )

The path "group.members" refers to the actual Collection instance. If you wanted to specify the Collection's implementation you could do the following:

new JSONDeserializer().use("group.members", LinkedList.class ).use( "group.members.values", ConcreteGroupable.class )

Maps support specifying key by append "keys" to the path and the values by appending "values" just like Collections. You don't have to specify the keys of the Map if they are one of the JSON native types like: String, or Double.

ObjectFactory

ObjectFactory are the underpinnings of the JSONDeserializer. All object creation pass through some type of ObjectFactory. Starting with 1.9 you can specify your own implementation and attach these to either paths or classes. ObjectFactories can be thought of as companions to Transformers in JSONSerializer.

There are 3 ways you can specify the use of an ObjectFactory:

For example, say we want to export dates as yyyy.MM.dd using the DateTransformer. We can do that easily using transform() method on JSONSerializer. Now let's read this back in by registering a DateTransformer as an ObjectFactory for "person.birthdate".

new JSONDeserializer().use("person.birthdate", new DateTransformer("yyyy.MM.dd") ).deserialize( people );

To use this pattern for all Dates you can register on the Date class. For example:

new JSONDeserializer().use(Date.class, new DateTransformer("yyyy.MM.dd") ).deserialize( people );

To use this pattern with an annotation would not be possible because we don't control the instantiation of the Transformer. This is a limitation of using Annotations that you should be aware of. However, there are plenty of use cases where Annotations are appropriate. Your transformers must have a no-arg constructor in order to be used in @JSON annotations.

public class SomeObject { @JSON( transformer = SomeFieldTransformer.class ) private SomeField someField; }

Deserializing has Rules for your Java Objects

All objects that pass through the deserializer must have a no argument constructor. The no argument constructor does not have to be public. That allows you to maintain some encapsulation. JSONDeserializer will bind parameters using setter methods of the objects instantiated if available. If a setter method is not available it will using reflection to set the value directly into the field. You can use setter methods transform the any data from json into the internal object structure you want. That way the JSON structure can be different from your Java object structure. The works very much in the same way getters do for the JSONSerializer.

When writing your own ObjectFactory use the Number interface when dealing with numeric values from the JSON stream. JSON doesn't specify any numerical precision on numbers so technically they can be very large. Larger than Integer, Long, Double, etc. So it's best to use the Number.longValue(), Number.intValue(), Number.doubleValue(), etc methods convert the JSON number into a primitive. Do not cast or use instanceof Long, Integer, Double, etc. It will not work. If you need a number larger than one of the 64 bit numbers you can use the JsonNumber interface to retrieve BigDecimal, BigInteger instances.

Direct Deserialization from a Stream

For simplicity Flexjson allows you to work with String objects, but it's more efficient to work directly with the stream the json data is coming through. Starting in 2.1 you can provide a java.io.Reader as a source of the json data. This cuts down the overhead of converting between String and whatever stream you want to use as the source of the json. Now it can go directly to the stream bypassing the conversion to String. Here is a simple example:

MyObject obj = new JSONDeserializer<MyObject>().deserialize( request.getReader() );

Deserializing into an Existing Object and Merging

This is supported out of the box as of 2.1

The JSONDeserializer supports deserializing json into an already existing instance. This allows you to support a primitive version of merging so prior values will be overwritten with the values in the json string, but anything not included in the json will remain untouched. This follows fairly simplistic rules, and doesn't do anything more complex than copy the data from json into your existing instance object. Here is an example:

Person charlie = new Person("Charlie", "Hubbard", cal.getTime(), home, work ); Person charlieClone = new Person( "Chauncy", "Beauregard", null, null, null ); Phone fakePhone = new Phone( PhoneNumberType.MOBILE, "303 555 1234"); charlieClone.getPhones().add( fakePhone ); String json = new JSONSerializer().include("hobbies").exclude("firstname", "lastname").serialize( charlie ); Person p = new JSONDeserializer<Person>().deserializeInto(json, charlieClone);

The variable p will reference the same instance as charlieClone. After deserializing into that instance, some of the values in p will be overwritten with values from charlie instance. However, we excluded the firstname and lastname fields so p will still have values "Chauncy" and "Beauregard" in it. Those will not be touched by the deserializer.

This shows how existing instances can be populated with data from json without needing to manually merge data from new objects to existing objects. Thus keeping boiler plate code for copying between two instances to a minimum. You can think of refreshing objects from the server or data stores as being a valid use case for this.

There are somethings to keep in mind. The return value will always be the exact same reference passed as the 2nd parameter to deserializeInto(). If there is a valid field in the json it will always overwrite what's in memory this includes null values too. This only supports deserializing into Java beans not collections.

Thread Safety and Reuse

Finally, JSONSerializer and JSONDeserializer instances can be reused to serialize/deserialize many objects of the same type. Once you instantiate the object you can reuse it across multiple threads so long as you don't call include or exclude, or use methods. Typically this isn't an issue since you might follow this pattern of instantiation:

public class PersonController { JSONSerializer personSerializer; public PersonController() { personSerializer = new JSONSerializer().include("addresses.zipcdoe"); } public String listPerson() { Person p = ....; return personSerializer.serialize( p ); } public String editPerson() { Person p = ....; return personSerializer.serialize( p ); } }
Thread safety is guaranteed with the default Transformers and ObjectFactories, but custom Transformers and ObjectFactories you write can have an impact on thread safety of the JSONSerializer/JSONDeserializer. Take care not to use internal state inappropriately, or use libraries that aren't thread safe. Both implementations of Transformer and ObjectFactory must be written such that multiple threads can be using the same instance at a time. Try and avoid synchronized blocks for best performance, but some cases might require locking.

Android Support

This is supported out of the box as of 2.1

Prior versions of the library did not work on Android because it used the very standard package java.beans.Introspector. Instead of waiting for Google to port that class over to become more compliant with the Java specification starting in 2.1 Introspector has been replaced so that we can enjoy using Flexjson on Android. For more ideas and information about how Flexjson can be used in Android see this post.

Maven 2

Release

FlexJson is avaialable on the central maven repository. In order to use flexjson in your maven enabled project simply add the following to your dependencies.

You can find the current release version here:
http://repo2.maven.org/maven2/net/sf/flexjson/flexjson/
<dependencies> ... <dependency> <groupId>net.sf.flexjson</groupId> <artifactId>flexjson</artifactId> <version>2.1</version> <!-- This may not be the current version. Check the above link --> </dependency> ... </dependencies>

Snapshot

If you wish to access a pre-release SNAPSHOT of flexjson you can add the following in the repositories section of your pom. Be sure to specify the SNAPSHOT version you wish to use in the version tag of the dependency.

<repositories> ... <repository> <id>sonatype.oss.snapshots</id> <name>Sonatype OSS Snapshot Repository</name> <url>http://oss.sonatype.org/content/repositories/snapshots</url> <releases> <enabled>false</enabled> </releases> <snapshots> <enabled>true</enabled> </snapshots> </repository> ... </repositories>
<dependencies> ... <dependency> <groupId>net.sf.flexjson</groupId> <artifactId>flexjson</artifactId> <version>2.1-SNAPSHOT</version> <!-- This may not be the most current SNAPSHOT version. Check the above link --> </dependency> ... </dependencies>

The End

That's really all you need to know about Flexjson. It's really just that simple and lightweight. Please give me some feedback. I'd love to hear about improvements or new features you would like added. Email me at charlie DOT hubbard ( I don't want spam so this is here to stop harvesters ) AT gmail DOT com.