Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Elegant mapping from POJOs to vertx.io's JsonObject?

Tags:

java

json

vert.x

I am currently working on a vertx.io application and wanted to use the provide mongo api for data storage. I currently have a rather clunky abstraction on top of the stock JsonObject classes where all get and set methods are replaced with things like:

this.backingObject.get(KEY_FOR_THIS_PROPERTY); 

This is all well and good for now, but it won't scale particularly well. it also seems dirty, specifically when using nested arrays or objects. For example, if I want to be able to fill fields only when actual data is known, I have to check if the array exists, and if it doesn't create it and store it in the object. Then I can add an element to the list. For example:

if (this.backingObject.getJsonArray(KEY_LIST) == null) {     this.backingObject.put(KEY_LIST, new JsonArray()); } this.backingObject.getJsonArray(KEY_LIST).add(p.getBackingObject()); 

I have thought about potential solutions but don't particularly like any of them. Namely, I could use Gson or some similar library with annotation support to handle loading the object for the purposes of manipulating the data in my code, and then using the serialize and unserialize function of both Gson and Vertx to convert between the formats (vertx to load data -> json string -> gson to parse json into pojos -> make changes -> serialize to json string -> parse with vertx and save) but that's a really gross and inefficient workflow. I could also probably come up with some sort of abstract wrapper that extends/implements the vertx json library but passes all the functionality through to gson, but that also seems like a lot of work.

Is there any good way to achieve more friendly and maintainable serialization using vertx?

like image 916
grdaneault Avatar asked Aug 17 '15 04:08

grdaneault


2 Answers

I just submitted a patch to Vert.x that defines two new convenience functions for converting between JsonObject and Java object instances without the inefficiency of going through an intermediate JSON string representation. This will be in version 3.4.

// Create a JsonObject from the fields of a Java object. // Faster than calling `new JsonObject(Json.encode(obj))`. public static JsonObject mapFrom(Object obj)  // Instantiate a Java object from a JsonObject. // Faster than calling `Json.decodeValue(Json.encode(jsonObject), type)`. public <T> T mapTo(Class<T> type) 

Internally this uses ObjectMapper#convertValue(...), see Tim Putnam's answer for caveats of this approach. The code is here.

like image 89
Luke Hutchison Avatar answered Sep 24 '22 01:09

Luke Hutchison


I believe Jackson's ObjectMapper.convertValue(..) functions don't convert via String, and Vert.x is using Jackson for managing JsonObject anyway.

JsonObject just has an underlying map representing the values, accessible via JsonObject.getMap(), and a Jackson serializer/deserializer on the public ObjectMapper instance in io.vertx.core.json.Json.

To switch between JsonObject and a data model expressed in Pojos serializable with Jackson, you can do:

JsonObject myVertxMsg = ... MyPojo pojo = Json.mapper.convertValue ( myVertxMsg.getMap(), MyPojo.class );

I would guess this is more efficient than going via a String (but its just a guess), and I hate the idea of altering the data class just to suit the environment, so it depends on the context - form vs performance.

To convert from Pojo to JsonObject, convert to a map with Jackson and then use the constructor on JsonObject:

JsonObject myobj = new JsonObject ( Json.mapper.convertValue ( pojo, Map.class ));

  • If you have implied nested JsonObjects or JsonArray objects in your definition, they will get instantiated as Maps and Lists by default. JsonObject will internally re-wrap these when you access fields specifying those types (e.g. with getJsonArray(..).

  • Because JsonObject is freeform and you're converting to a static type, you may get some unwanted UnrecognizedPropertyException to deal with. It may be useful to create your own ObjectMapper, add the vertx JsonObjectSerializer and JsonArraySerializer, and then make configuration changes to suit (such as DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES in Jackson).

like image 35
Tim Putnam Avatar answered Sep 22 '22 01:09

Tim Putnam