Subtitle:
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:
A JsonConfig object can be suited by special formatters which later will be given to the fromObject method:
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
It's no matter when using final classes, but we simply cannot apply substitution principle (LSP) generally when using getClass-match. In this case,
Fortunately it's possible to replace the default search mechanisn too:
This solution still have drawbacks. Say, if I register
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