This package contains a JSON API and a JSON/POJO codec.
This package provides two ways to deal with JSON documents. A simple API mapping the JSON document to a hierarchical set of name value pairs and a codec to map between JSON and sets of plain Java objects using the reflection API. Both methods will be explained in the following sections.
Using this method, you get access to the JSON content via the classes JSONObject and JSONArray. The Parser creates a hierarchy of instances of these classes according to the given JSON document.
In both cases, values are decoded from the given string and stored in scalar types such as Integer, Double, Boolean etc.
The following JSON document:
{ "member1": 1.0, "member2": true, "member3": [ { "m1": "Hello my friend!" }, { "m1": null } ] }Can be parsed with an instance of the class Parser of this package:
Parser p = new Parser(jsonString); JSONObject json = p.parse();
The given example above would result in an object hierarchy like this:
Access to the member variables is provided via get
and set
methods.
if ((boolean)json.get("member2")) { JSONArray array = (JSONArray)json.get("member3") JSONObject object = (JSONObject)array.get(1); object.set("m1", "You'r not my friend, dude!"); }
Both classes (JSONObject and JSONArray) provide a method toString
to
receive the JSON document.
String jsonString = jsonObject.toString();
Using this method is based on the use of the JSONCodec in this package. It allows you to map JSON documents directly to Java objects and vice versa. This of course requires that the class of the object contains the member variables which exist in the JSON document.
Important Rules:
static
and transient
members will be ignored.enum
(not supported yet).Note: The codec internally uses the Parser class to turn the JSON document in instances of JSONObject and JSONArray before it maps it to your Java object. A better solution (possibly coming in future) is to parse it directly to the object.
class A { double member1; boolean member2; B[] member3; /* hide this from the JSON codec */ transient int dont_mind_me; } class B { String m1; }
The JSONCodec is used to decode the given JSON document to your Java object.
JSONCodec codec = new JSONCodec(); A obj = new A(); codec.decodeObject(jsonString, obj);
Access is much simpler this way, as the next code fragment demonstrates, which realises the same functionality as in the example above.
if (obj.member2) { obj.member3[1].m1 = "Now we'r talking!"; }
The codec is also used to turn any Java object into a JSON document.
String jsonString = codec.encodeObject(a);
static
or transient
As a feature, static
and transient
members of an object
will be ignored by the codec.
The transient
modifier allows you to control which members
will be ignored by the codec which (for example) comes in handy if you have
to resolve cyclic dependencies in your data model or you have runtime variables
as members which you don't want to be persistent.
The static
members have been excluded because they are actually
not a member of the object but of the class. That means, there is no point in
storing them in every object.
transient
as shown in the example below.
class A { B refB; } class B { transient A refA; }
class
class MySuperClass {} class A extends MySuperClass {} class B extends MySuperClass {} /* class with a reference on any of those subclasses */ class PolymorphismUser { MySuperClass ref_on_A_or_B; }
JSON does not know anything about classes and is not type save. Thus, the parser requires additional information to determine the actual type of an object if it does not match with the type of the variable which will receive the reference.
Since Java has built-in runtime type information on each object stored in a global
variable for each class which is called "class
", this attribute name
is implicitly reserved and can be used for this purpose if required.
For this purpose, you can configure the JSONCodec to use the class
attribute.
Refer to class JSONCodecConfiguration
to do so.
When enabled, the codec will compare the type of a reference and the type of
a referenced object and in case of differences (inheritance) it will
automatically add an attribute class
which holds the name of the class, when encoding.
When decoding, the codec will in turn search for an attribute class
and use the type
information to instantiate the proper object.
/* given an object with reference on A while reference is of type SuperClass */ PolymorphismUser object = new PolymorphismUser(); object.ref_on_A_or_B = new A(); /* configure and instantiate a codec which supports the "class" attribute. */ JSONCodecConfiguration cfg = new JSONCodecConfiguration(); cfg.considerClassAttribute = true; JSONCodec codec = new JSONCodec(cfg); /* encode object into a json string */ String json = codec.encodeObject(object); /* and decode it again using a codec with the same configuration */ PolymorphismUser o2 = (PolymorphismUser) codec.decodeObject(json, PolymorphismUser.class); /* member o2.ref_on_A_or_B is of type A again. */ assert object.ref_on_A_or_B.getClass().equals(o2.ref_on_A_or_B.getClass());
enum
s require to be unique (of course). The codec does not know
yet about enums, that's why it will literally store enums in the JSON document
by their values and loads them as such, when decoding. As a result two enums
with the same values are now different, because the test on equality compares
their memory addresses and not their values. However, a future version of
org.cakelab.json will consider this.