user1482309
user1482309

Reputation: 309

JAXB serialize JsonElement field in POJO

I have a POJO class like:

class Cat {
  public Cat() {}
  private String name;
  private JsonElement arbitraryProperties;
}

I am using Jackson (JAXB) and cat is a resource I want to import and export.

GET /cats/{id}
POST /cats

Export is working fine. Import did not work because JsonElement is abstract. So I added @JsonDeserialize(using = MyJsonElementDeserialize.class) annotation to the arbitraryProperties field.

The MyJsonElementDeserialize class looks like:

public class JsonElementDeserialize extends JsonDeserializer<JsonElement> {

public JsonElementDeserialize() {
}

@Override
public JsonElement deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException {
    return new JsonObject(); // here I need to create my object later
}

}

When I now call POST /cats with payload:

{
  "name": "Mia",
  "arbitraryProperties": {
        "a": 3,
        "b": [],
        "c": {
            "d": "race"
        }
   }
 }

Jacksons returns error code 400:

Unrecognized field "a" (class Cat), not marked as ignorable (1 known properties: "name"]).

Where is my mistake? How can I have a JsonElement in my POJO that gets automatically serialized and deserialized?

Upvotes: 1

Views: 435

Answers (4)

ahll
ahll

Reputation: 2417

If could, I would change JsonElement to JsonNode, and use “Tree Model” of Jackson.

Upvotes: 1

StaxMan
StaxMan

Reputation: 116630

The problem here is that deserializers must consume all content that they cover: that is, if your deserializer does not "read" JSON content that would bind (contents of a here), they are left for further processing.

Solution should be easy, just call

jsonParser.skipChildren();

in deserialize method and it will skip over current input token, as well as all of its contents (if its JSON Object or Array).

Alternatively, if you wanted to use contents, you could use

jsonParser.readValueAsTree();

and there are a few other convenience methods.

Upvotes: 1

Manos Nikolaidis
Manos Nikolaidis

Reputation: 22264

Here is a simple solution using a setter method in Cat to transform something Jackson parses (e.g. Map<String, Object>) to a GSON JsonElement type:

void setArbitraryProperties(Map<String, Object> properties) {
    arbitraryProperties = new Gson().toJsonTree(properties).getAsJsonObject();
}

You can do something similar inside a custom deserializer if you can't use a setter here. I find setters/getters simpler for transformations with Jackson.

Note that a Gson instance is employed to convert the Map to JsonElement (a JsonObject actually). Docs for toJsonTree.

Since you have a field of a type defined in GSON and therefore you have GSON as a dependency and inexorably need GSON to create this field, you may consider Sharon's suggestion and skip Jackson and use GSON for deserialization.

Upvotes: 4

Biraj Sahoo
Biraj Sahoo

Reputation: 44

Try to use "produces=MediaType.APPLICATION_JSON_VALUE" and @ResponseBody in controller method if you are using Spring mvc.

Upvotes: 0

Related Questions