Thursday, October 08, 2009

Handling Base64-encoded binary data with Jackson

Hopefully by now you know that Woodstox can handle base64-encoded binary data for your XML use cases. You may even know that Jackson can do the same for JSON (notice that "g.writeBinary()" call in Jackson Tutorial?)

But there is actually bit more to know about base64 functionality here. Let's first review core Base64 handling with 3 main processing models Jackson supports.

1. Handling base64-encoded binary with Streaming API

Assuming you get JSON content like:

  "binary" : "hc3VyZS4="

you can get binary data out by, say:

  JsonParser jp = new JsonFactory().createJsonParser(jsonStr);
  jp.nextToken(); // START_OBJECT
  jp.nextValue(); // VALUE_STRING that has base64 (skips field name as that's not a value)
  byte[] data = jp.getBinaryValue();

And if you want to produce similar data, you can do:

  byte[] data = ...;
  StringWriter sw = new StringWriter();
  JsonGenerator jg = new JsonFactory().createJsonGenerator(sw);
  jg.writeFieldName("binary"); // 1.3 will have "writeBinaryField()" method
  jg.writeBinary(data, 0, data.length);

2. Handling base64-encoded binary with Data Binding

But where JsonParser and JsonGenerator make access quite easy, ObjectMapper makes it ridiculously easy. You just use 'byte[]' as the data type, and ObjectMapper binds data as expected.

  static class Bean {
    public byte[] binary;

  ObjectMapper mapper = new ObjectMapper();
  Bean bean = mapper.readValue(jsonStr, Bean.class); 
  byte[] data = bean.binary; // Want to serialize it? Sure:
  String outputStr = mapper.writeValueAsString(bean); // note: Jackson 1.3 only; otherwise use StringWriter

3. Handling base64-encoded binary with Tree Model

Handling binary data is almost as easy with Tree Model as well:

  JsonNode object = mapper.readTree(jsonStr);
  JsonNode binaryNode = object.get("binary");
  byte[] data = binaryNode.getBinaryValue();

  // or construct from scratch, write?
  ObjectNode rootOb = mapper.createObjectNode();
  rootOb.put("binary", rootOb.binaryNode(data));
  outputStr = mapper.writeValueAsString(rootOb);

4. Additional Tricks

(DISCLAIMER: following features have been tested with Jackson 1.3, not yet released)

But what if you actually just want to encode or decode binary data to/from Base64-encoded Strings, outside context of JSON processing?

Turns out that you can do simple encoding and decoding quite easily. And as an additional bonus, Jackson's strong focus on performance means that the underlying codec is very efficient, even for "extra-curricular" use (where output buffering is not utilized as it is for incremental JSON processing).
In fact, it may just be faster than alternative commonly used processing toolkits.

Anyway: to encode arbitrary binary data as, you can do:

  import org.codehaus.jackson.node.BinaryNode;

  BinaryNode n = new BinaryNode(byteArray);
  String encodedText = n.getValueAsText();

// or as one-liner: encodedText = new BinaryNode(data).getValueAsText();

and to decode given Base64-encoded String, you can retrieve contained binary data by:

  import org.codehaus.jackson.node.TextNode;

  TextNode n = new TextNode(encodedString);
  byte[] data = n.getBinaryValue();

// or as one-liner: data = new TextNode().getBinaryValue();
// or, if encoded using non-standard Base64 variant, try: data = n.getBinaryValue(Base64Variants.MODIFIED_FOR_URL);

Useful? Possibly -- no need to include Jakarta Commons Codec just for Base64 handling, if you happen to use Jackson already.

blog comments powered by Disqus

Sponsored By

Related Blogs

(by Author (topics))

Powered By

About me

  • I am known as Cowtowncoder
  • Contact me
Check my profile to learn more.