Skip to main content

Json-lib vs Hibernate

Subtitle: java.util.Date vs java.sql.Timestamp

One of my projects has a data path where Hibernate entities are converted to Json objects by using json-lib. I have some special formatters, e.g. for Date objects for having an easy readable format instead of this:

{"date":1,"day":4,"hours":1,
"minutes":0,"month":0,"nanos":0,
"seconds":0,"time":0,"timezoneOffset":-60,"year":70}


A JsonConfig object can be suited by special formatters which later will be given to the fromObject method:
JsonConfig cfg = new JsonConfig();  
cfg.registerJsonValueProcessor(
java.util.Date.class, new JsonValueProcessor() {
private static final SimpleDateFormat SDF =
new SimpleDateFormat("yyyy-MM-dd");
public Object processArrayValue(Object value, JsonConfig c) {
return format((Date)value);
}
public Object processObjectValue(
String key, Object v, JsonConfig c) {
return format((Date)v);
}
public static String format(Date date) {
return SDF.format(date);
}
});
JSONObject json = JSONObject.fromObject(objToConvert, cfg);

The problem is, that the code above sometimes doesn't work, because json-lib's default search mechanism regarding value formatters is based on exact class match, however, Hibernate sometimes replaces values for something else, say java.util.Date to java.sql.Timestamp.

It's no matter when using final classes, but we simply cannot apply substitution principle (LSP) generally when using getClass-match. In this case, fromObject won't convert descendants properly when we register a formatter for the superclass. An ugly solution is to register formatters for all descendant classes explicitly but this violates Open Closed Principle. When a new descendant appears, we must amend the code which registers the formatters.

Fortunately it's possible to replace the default search mechanisn too:
cfg.setJsonValueProcessorMatcher(
new JsonValueProcessorMatcher() {
public Object getMatch(Class target, Set set) {
if( target != null && set != null) {
for(Object obj : set) {
Class c = (Class) obj;
if(c.isAssignableFrom(target)) return c;
}
}
return null;
}
});

target is the runtime class type of the object to convert, set contains the registered formatters' keys. getMatch contract method returns the key for the appropriate formatter or null if no such one.

This solution still have drawbacks. Say, if I register java.util.Date and java.sql.Timestamp too, the matcher will randomly find them, based on the order of the keys in the set. So, some logic is missing which searches for closest match or something like that. But it's good for a demo code.

Comments

Popular posts from this blog

Client's transaction aborted

I've met the above error message using a Wicket 1.2 / EJB3 intranet application under Glassfish v2 . Here is the more particular head of the stack trace: javax.ejb.TransactionRolledbackLocalException: Client's transaction aborted at com.sun.ejb.containers.BaseContainer.useClientTx(BaseContainer.java:3394) at com.sun.ejb.containers.BaseContainer.preInvokeTx(BaseContainer.java:3274) at com.sun.ejb.containers.BaseContainer.preInvoke(BaseContainer.java:1244) at com.sun.ejb.containers.EJBLocalObjectInvocationHandler.invoke(EJBLocalObjectInvocationHandler.java:195) at com.sun.ejb.containers.EJBLocalObjectInvocationHandlerDelegate.invoke(EJBLocalObjectInvocationHandlerDelegate.java:127) This exception raised on the integration server sometimes, randomly, for simple page fetch operations. After pressing reload on the browser, the operation was usually successful. I couldn't reproduce the failure on the local machine where I regularly restart the app server and...

Standup

Recently I was asked if it makes sense to do standups. Is it just a formal waste of time? Wouldn't it be more useful to spend the same amount of time by actual work? This is how our standup looks like, this is how the work would look like without it according to me and this is why I think it's worth doing standups: Standup optionally starts with a half-minute long announcement by the Scrum Master if somebody is missing and when will be this person available again. Without standup:  We could check out this information from a well-prepared shared calendar but unexpected lates or illnesses which are missing from the calendar would require a little bit more communication and irrelevant discussion during the day. It would cause some delay for sure. Then we look at the burn-down chart of the sprint and to the status of the latest nightly build. Is it stable, what about automated tests which were run last night? We make a common standpoint in one minute which is clear a...

Installing Firefox Java plugin on Ubuntu Linux for Dummies

As a Windows user, I was amused how not easy to install Java on Ubuntu Linux (10.04) nor installing Java (1.6) plugin into Firefox (3.6). It's not nearly a download-next-next-finish task. Additionally, digging into forums I found a lot outdated solutions which I tried and soon realized that they doubtlessly aren't working. Here is the one which worked for me: First, open a terminal window: Applications menu -> Accessories -> Terminal. Type the followings (you will be promted for admin password, because sudo prefix means you want to do something in the name of the super (or sytem) user): sudo add-apt-repository "deb http://archive.canonical.com/ lucid partner" sudo aptitude update sudo aptitude install sun-java6-jdk Then you have to select things with arrows and pressing enter. Then Timothy says in the forum you have to type this, but I didn't need it: sudo update-alternatives --config java Now you can check if java is installed by typing java -version . ...