By now, Jackson is
becoming widely-known for its lightning-fast streaming JSON parser, as
well as for its powerful, intuitive and efficient data binding
functionality. But wait! There is more!
(and when you are convinced you need Jackson, head straight to Download
page)
1. Background
Has this ever happened to you? You need an array of ints, and all you
got is this dingy little List of Strings (representing numbers)! You
would think JVM, as smart as it is, could quickly whip up a conversion
to "do the needful"... but no. In fact, even simpler conversions like
number to/from String, String to/from boolean, Sets to/from Lists and so
on are irritating: easy to solve, sure, but with too much monkey code.
And don't even get me started on other conversions between encodings
like base64 to byte arrays; dumping Object fields as Maps; or building
Objects from Maps (possibly read from properties files). There are tons
of simple tasks that should be made even simpler.
2. What if...
Ok, now: let's round up some facts related to POJO serialization and
Jackson:
-
Jackson is great at serializing all kinds of Java objects as JSON
-
Jackson is awesome at deserializing JSON into Java objects
So: what if... say... you serialized, List<String> into JSON and...
lessee... deserialized it back to.... int[]... what would happen?
Conversion! (as long as Strings indeed contain numbers -- if not, what
would happen is an exception of some kind)
Ah! I see, so, you are saying that serialize+deserialize == conversion!
Like:
//ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(myStringList);
int[] intArray = mapper.readValue(json, int[].class);
Presto!
3. Jackson 1.3 gets it
With version 1.3, there is something that simplifies above procedure by
50%:
int[] intArray = mapper.convertValue(myStringList, int[].class);
(yes, you should have seen that one coming)
3.1. Conversions: Basic types
Primitive type conversions obviously work:
Boolean b = mapper.convertValue("true", Boolean.class);
Date d = mapper.convertValue("2009-10-10T12:00:00.00+0800");
although are not exactly any shorter than equivalent idioms you would
use. But they serve as basis for other conversions, for Lists, Maps and
arrays of primitives and wrappers.
3.2. Conversions: Containers
Similarly, containers with various content types work as expected:
ArrayList<Integer> ints = mapper.convertValue(new Object[] { 13, "1", Integer.valueOf(3) });
Set<String> uniq = mapper.convertValue(new String[] { "a", "b", "a" }); // would produce set with 2 entries
3.3. Conversions Base64<->binary
And if you want to encode binary data, you can do:
String encoded = mapper.convertValue(new byte[] { 1, 2, 3 });
byte[] decoded = mapper.convertValue(encoded, byte[].class);
(usually reading from a File or such)
3.4. Beans to Maps and back
Finally, you can also convert simple Java beans (or more generally
POJOs) into Maps or JSON trees:
Map fieldMap = mapper.convertValue(myBean, Map.class);
MyBean bean = mapper.convertValue(fieldMap, MyBean.class);
// or read from properties file
Properties props = new Properties();
props.load(new FileInputStream(file));
MyBean bean2 = mapper.convertValue(props, MyBean.class);
// and can convert to a JSON tree as well:
JsonNode rootNode = mapper.convertValue(bean2);
And you can obviously configure bean types with annotations (regular and
mix-in annotations) as you like, as necessary for conversion you want to
do.
Neat stuff, eh?
4. What does this have to do with JSON?
Good question. Nothing, really. :-)
That is: there is no requirement for the intermediate JSON generation;
and in fact, for future Jackson versions, there will be improvements to
allow use efficient intermediate data structure for these conversions.
And conveivably one could even refactor functionality into separate bean
conversion package, if conversions are widely used without actual JSON
processing.
5. Got better use cases?
I hope someone out there can come up with even better examples of this
power. If so, let me know!
One area that I hope to improve upon is that of converting java.util.Properties
into POJOs. Although sample above works, it does not deal with naming
convention of "refField.anotherField.field = 3", which is one natural
way to represented nested structures. It should be made to work; just
needs little bit of name mangling; especially when trying to handle
arrays and lists ("object.listField.4 = abx").