Atomicity Technology, Programming and Learning

8Jan/121

Custom serialization using Jackson

Jackson JSON provides many different ways to customize the serialization process of a class, most of these methods utilise annotations on the classes themselves. I recently had the requirement to customize the serialization of a class when I had no access to change the source code of that class. That meant that using annotations wasn't an option and I had to find another way.

After a fair amount of googling I stumbled across the following page (written by the author of Jackson):

Every day Jackson usage, part 3: Filtering properties

There are 7 options detailed in this post, 6 of which use annotations on the class itself. Option 7 is my only option then. Here it is:

7 . Most extreme way to filter out properties: BeanSerializerModifier

And if ability to define custom filters is not enough, the ultimate in configurability is ability to modify configuration and configuration of BeanSerializer instances. This makes it possible to do all kinds of modifications (changing order in which properties are serialized; adding, removing or renaming properties; replacing serializer altogether with a custom instance and so on): you can completely re-wire or replace serialization of regular POJO ("bean") types.

This is achieved by adding a BeanSerializerModifier: the simplest way to do this is by usingModule interface. Details of using BeanSerializerModifier are more advanced topic; I hope to cover it separately in future. The basic idea is that BeanSerializerModifier instance defines callbacks that Jackson BeanSerializerFactory calls during construction of a serializer.

Right, so now I know what I need to use but there's no details on actually how to use it.

Classes of interest

The 3 classes of concern are:

  • org.codehaus.jackson.map.ser.BeanSerializerModifier
  • org.codehaus.jackson.map.Module
  • org.codehaus.jackson.map.ObjectMapper

BeanSerializerModifier

When extended this class provides 4 entry points which can be used to customize the serialization process:

  1. changeProperties() - Implementations can add, remove or replace any of passed properties.
  2. orderProperties() - Implementations can change ordering any way they like.
  3. updateBuilder() - Implementations may choose to modify state of builder, or even completely replace it.
  4. modifySerializer() - Implementations can modify or replace given serializer and return serializer to use.

Module

This is a simple interface used for registering extensions with ObjectMapper. The setupModule() method will be called during initialization and this provides an instance of Module.SetupContext. The method addBeanSerializerModifier() on that context object can then be used to register a custom BeanSerializerModifier.

ObjectMapper

This is the main class within Jackson for serializing and deserializing objects, the Module is registered with this object.

Usage

Firstly you extend BeanSerializerModifier and override the serialization process in whatever custom way you wish. For example you might wish to reorder the fields and also exclude some. The API for this is well documented, read the javadocs.

Next you need to wire your modifier class into the ObjectMapper and you do this by creating a Module and then registering that with the ObjectMapper using registerModule(). In the setupModule() method in your module you register the new BeanSerilizerModifier.

Example Code

To show how this works in practice I've put together a small project that implements a FilteringBeanSerializerModifier, which changes the serialization behaviour of ObjectMapper by filtering out fields on specified classes.

You can find the source code on GitHub here:

https://github.com/ultraflynn/jackson-custom-serialization

Tagged as: , , 1 Comment
17Nov/110

Unix disk usage commands

When a unix filesystem fills up the first question asked is usually, "Who is using the most disk space?". Naturally there's many ways of finding out but this works for me.

du -hc * | sort -n | grep "[0-9]G"

This will look for files being measure in Gigabytes and give you a list of the directories using the most. If your file system has smaller files on them substitute the "G" for "M".

If that produces too many result it can be limited this this.

du -hc --max-depth=1 *snapshots* | sort -n | grep "[0-9]G" | tail

This will only go to one level of depth in the directory structure and will be looking only at directories with the word "snapshots" in them.

This also produces a grand total of files. This will always be displayed last in the output because the sort is numeric and it's the highest number.

Tagged as: No Comments
13Nov/110

Ivy and Maven Caching

The dependency management in Play is a  bit of an afterthought which is being address in V2.0 but for those using V1.X we're stuck with using a Play yaml front-end to Ivy. Most of the time it's absolutely fine but when you are doing local development of non-Play libraries which are stored in a corporate Maven repository (such as Nexus) you end up having problems.

The issue stems from the way in which Ivy (and therefore Play) uses those Maven repositories. Maven repositories are configured in the dependencies.yaml file which supports a subset of the behaviour permitted in the .ivy2/ivysettings.xml configuration file. Once Ivy has resolved the dependency it stores a copy in it's local cache to avoid having to load it from the Maven repository again. And this is the problem. What if the artifact changes in the repository? Generally that's not the case, usually you'd expect a new version of an artifact to be deployed to the repository. That is unless it's a SNAPSHOT version, i.e. an ongoing development version, and this is exactly the situation when you are developing a non-Play library and a Play application at the same time.

Consider the following situation; you are working on a Play application which uses a library that you are also developing, which is not a Play application. You change that library and you perform a Maven "install" to compile and place the snapshot artifact into you local Maven repository. Then you run your Play application to see whether your Play application works using the new version of the library. This isn't going to work because Ivy has cached the library in it's own local repository. It doesn't look for changed artifacts in the Maven repository, and this makes joint development of Play! applications and non-Play libraries a little tricky.

There is a workaround though, instead of defining the Maven repositories through the dependencies.yaml file you define them as a chained resolver in the .ivy2/ivysettings.xml file and there it's possible to define a "changingPattern" on the resolver which indicates that the resolver should re-check the dependency if it's a SNAPSHOT.

Here's how to do this:

<?xml version="1.0" encoding="UTF-8"?>
<ivysettings>
  <settings defaultResolver="Local Ivy Cache"/>

  <resolvers>
    <ibiblio name="Local Maven Repository" m2compatible="true" usepoms="true"
             root="file:///${user.home}/.m2/repository/"/>

    <ibiblio name="Remote Nexus Repository" m2compatible="true" usepoms="true"
             root="http://nexus.remote.url:8080/nexus/content/groups/code/"/>

    <chain name="Local Ivy Cache" changingPattern=".*-SNAPSHOT">
      <resolver ref="Local Maven Repository"/>
      <resolver ref="Remote Nexus Repository"/>
    </chain>
  </resolvers>
</ivysettings>

(I'm working on a pure Maven solution for Play so that Ivy isn't involved at all and then this whole problem goes away. It's going to be interesting to see what this process is going to look like when Play v2.X comes along)
13Nov/110

Unix tail highlighting

Often whilst tailing a log file in Unix you want to have the specific thing you are watching for highlighted as it appears. Here are two ways of achieving this.

Option 1 - tail/perl

t() {
tail -f -n5000 $1 | perl -pe "s/$2/\e[1;31;43m$&\e[0m/g"
}

Option 2 - sed

sed filename.txt
/string-to-watch-for
F

Tagged as: , , , No Comments