Reputation: 309
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
Reputation: 2417
If could, I would change JsonElement to JsonNode, and use “Tree Model” of Jackson.
Upvotes: 1
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
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
Reputation: 44
Try to use "produces=MediaType.APPLICATION_JSON_VALUE" and @ResponseBody in controller method if you are using Spring mvc.
Upvotes: 0