Hi!
I’m playing around with making our Mapper more flexible by allowing more
freedom for writing and parsing JSON from and to arbitrary Objects.
Currently Mapper contains both the logic of writing and reading Objects. As a
result our Mapper class became pretty huge already. > 1k LOC is not something
we like to maintain for a long time.
I like to split that functionality into 3 different classes
1.) MapperConfig. That basically contains all our internal flags. It gets
constructed in the MapperBuilder and passed to the created Mapper
2.) JsonbParserImpl (extends JsonbParser). It will contain all the Object
reader parts of the Mapper. It will also have a method JsonParser
getJsonParser(); which allows direct access to the JsonParser.
JsonbParser does _not_ extend JsonParser! I played around with it but as
JsonParser methods all return a JsonParser and obviously _not_ a JsonbParser
this woud defeat builder-like programming. By having a separate method it’s
more obvious what happens.
3.) JsonbGenerator. See 2, but for writing. It will contain methods to write
full Objects (and Arrays?).
Mapper will still have the methods for end-users to writeObject, writeArray +
reads with OutputStream, Writer and AsString. But the full control is done in
JsonbParser and JsonbWRiter
Why do I need all that?
The root of this demand was to create a more flexible way to decide what gets
written and read from Object <-> JSON. I thought about the following interface
public interface ObjectConverter<T> {
void writeJson(T instance, JsonbGenerator jsonbGenerator);
T fromJson(JsonbParser jsonbParser, Type targetType);
}
Let’s look at writing JSON from some class:
public static class Dog {
private String name;
private Dog father;
private Dog mother;
}
public static class Beagle extends Dog {
}
public static class Poodle extends Dog {
boolean hairCut = false;
}
public static class Mutt extends Dog {
}
and the test code:
Poodle mum = new Poodle();
mum.setName("Rosa");
mum.setHairCut(true);
Beagle dad = new Beagle();
dad.setName("Gnarl");
Beagle grandPa = new Beagle();
grandPa.setName("Wuffi");
dad.setFather(grandPa);
Mutt snoopie = new Mutt();
snoopie.setName("Snoopie");
snoopie.setFather(dad);
snoopie.setMother(mum);
String json = mapper.writeObjectAsString(snoopie);
The Mapper will create a JsonbGenerator and hand over snoopy to it.
Whenever the JsonbGenerator hits a type which we have a Object Converter for
then it will call writeJson(T instance, JsonbGenerator jsonbGenerator);
In your own Converter you can now e.g. do
jsonbGenerator.getJsonGenerator().write(„//javaType",
instance.getClass().getName());
jsonbGenerator.writeObject(instance);
or also probably
jsonbGenerator.getJsonGenerator().write(„//javaType“,
instance.getClass().getName());
jsonbGenerator.getJsonGenerator().writeStartObject(„dog“);
jsonbGenerator.writeObject(instance);
jsonbGenerator.getJsonGenerator().writeEnd();
How does that look to you?
LieGrue,
strub