I tried deserialize following json to POJO.
{
"foo": {
"key1":"dummy",
"key2":"dummy"
},
"bar": {
"key1":"dummy",
"key2":"dummy",
"key3":"dummy"
},
"bazKey1":"dummy",
"bazKey2":"dummy",
"bazKey3":"dummy",
"bazKey4":"dummy"
// Many others....
}
You can see above strange baz properties...
But I want to treat baz as an object like foo and bar.
public class Pojo {
private Foo foo;
private Bar bar;
private Baz baz;
// Many others....
}
However, I just found poor solution which uses custom deserializer.
Poor solution
#Override
public Pojo deserialize(JsonParser p, DeserializationContext ctxt) throws Exception {
ObjectCodec codec = p.getCodec();
JsonNode node = codec.readTree(p);
Baz baz = new Baz.Builder()
.key1(node.get("bazKey1").textValue())
.key2(node.get("bazKey2").textValue())
.key3(node.get("bazKey3").textValue())
.key4(node.get("bazKey4").textValue())
.build();
// We have to write annoying (setter/constructor/builder) instead of below method.
// return codec.treeToValue(node, Pojo.class);
return new Pojo.Builder()
.foo(foo)
.bar(bar)
.baz(baz)
.other(other)
.other(other)
.other(other) // Many others...
.build();
}
This solution forces us to use annoying (setter/constructor/builder).
How to deserialize fields into object using jackson?
Additionally, this POJO is Immutable object.
The point of Jackson is that you're not in this situation. The very reason why you use Jackson at all is because when you have a Baz Java object, it's represented with a baz JSON property that contains an object, and when you have several string JSON properties, they are represented in Java with several String fields of the same name.
If that's not the situation you're in, then there is no reason to consider Jackson. Use any JSON parsing library, and build your Java objects from the JSON tree. You can use Java reflection to discover the Java class' fields/methods and set/call them based on the JSON properties' names.
Related
I am using Spring RestTemplate to communicate with an provided REST service that delivers JSON. To Map the response I am using Jaxon, but I will gladly switch to anything else that works.
I would like to create an POJO that contains sub-content of the delivered data but in a different Structure.
It boils down to this:
Source: { "a": "val_a", "b" : {"c" : "val_c", "d": "val_d"}}
#JsonIgnoreProperties(ignoreUnknown = true)
class Foo {
// should contains the content of `"a": "val_a"`
// but contains null
private Baa;
// getter and setter
}
#JsonIgnoreProperties(ignoreUnknown = true)
class Baa {
private String a;
// getter and setter
}
// This should be the operation that is done internally by Spring when calling
// ResponseEntity<Foo>response = restTemplate.exchange(url, HttpMethod.GET, entity, Foo.class);
// response.getBody();
private Foo read(String s) {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(Include.NON_NULL);
mapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES);
return mapper.readValue(s, Foo.class);
}
The result of the deserialization is an empty Baa object. The actual JSON and POJO Object structure is more complex but this sums it up.
Any Technology that achieves this would be welcome.
The only possibility I came up with is deserializing the JSON in the provided structure and write a Converter class that generates the desired Object but I was hoping to avoid this.
----- Update/clarification ------
The problem is, that the property a should be mapped within class Baa, which lies within Foo but is provided in the root path (is this the right term?) of the provided JSON objekt
public class Foo {
private String a;
private B b;
// getters setters
}
public class B {
private String c;
private String d;
// getters setters
}
Should map with no additional annotations with your code. If you're having a particular code with a non trivial example then post your actual code in whole.
Update on your clarification: no you can't do that with annotations as I said in my comment. You will have to write the custom deserialiser. Check out this answer: Jackson: is it possible to include property of parent object into nested object?
If you don't want to write the java bean that rappresent the JSON structure, you have to use a different library. Jackson forces you to create a java structure that reflects the JSON structure. In my opinion Jackson works great and i suggest you to use it, but the alternative could be JSON library.
With this one you can select only the element you want from the json, and map it to the bean you want.
Little example:
JSONObject response = new JSONObject("{\"a\": \"val_a\", \"b\" : {\"c\" : \"val_c\", \"d\": \"val_d\"}}");
JSONObject bObject = response.getJSONObject("b");
String cElement = (String) elenco.get("c");
The value of bObject is {"d":"val_d","c":"val_c"}, and the value of cElement is val_c
This libray uses JSONObject and JSONArray generic objects, to map the content of the json to a java object.
i've been wondering how to correctly solve this problem
I have a data model like this:
Class B
String fieldB1;
String fieldB2;
Class A
String fieldA1;
String fieldA2;
List<B> fieldA3;
(and then another third class which has the same hierarchy as the other with fields and a list of A objects, but for simplicity let's stick with A and B)
Now on the other side i have to deserialize these classes in classes with the same name and params just with different data types
So ^ must read as:
Class B
int fieldB1;
double fieldB2;
Class A
float fieldA1;
float fieldA2;
List<B> fieldA3;
Since i'm not experienced, my first guess was to write customs Deserializer in jackson for A and B, and when I deserialize a Class like B which does not have reference to other classes with custom deserialization methods, the conversion is easy.
But what about creating a custom deserializer for Class A? When I have to deserialize the fieldA3, aka the list of B objects, how should I operate? Should try to call in some way in the ClassACustomDeserializer the ClassBCustomDeserializer? How to do that?
Or is there another simpler solution to just tell jackson to transform some String fields in some other types based on my personal mapping?
This is how i would deserialize B
public class BDeserializer extends StdDeserializer<B> {
public BDeserializer() {
this(null);
}
public BDeserializer(Class<?> vc) {
super(vc);
}
#Override
public B deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode node = jp.getCodec().readTree(jp);
int fieldB1= node.findValue("fieldB1").asInt();
double fieldB2= node.findValue("fieldB2").asDouble();
return new B(fieldB1,fieldB2);
}
}
Jackson is smart enough to convert the text value to an appropriate numeric type, so it should be able to deserialize a JSON like:
{ "fieldB1": 10, "fieldB2" : "0.333" }
to your
Class B
int fieldB1;
double fieldB2;
just nice, even w/o using a custom deserializer.
If you want to stick with a custom deserializer, for whatever reason,
you can either use JsonNode.traverse() to create a sub-parser:
JsonParser parser = node.findValue("fieldA3").traverse();
parser.setCodec(jp.getCodec());
List<B> list = parser.readValueAs(new TypeReference<List<B>>() {});
or navigate the token stream yourself, instead of using find:
while(jp.nextToken() != JsonToken.END_OBJECT) {
if(jp.currentToken() == JsonToken.FIELD_NAME) {
switch (jp.getCurrentName()) {
//...
case "fieldA3":
jp.nextToken();
list=jp.readValueAs(new TypeReference<List<ClassB>>() {}));
break;
}
}
}
the latter should be more efficient, if performance is of concern.
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?
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.
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.
If could, I would change JsonElement to JsonNode, and use “Tree Model” of Jackson.
Try to use "produces=MediaType.APPLICATION_JSON_VALUE" and #ResponseBody in controller method if you are using Spring mvc.
I am trying to deserialize a object ref ($ref) using ObjectMapper.
public class Foo {
#JsonProperty("bar")
private Bar bar;
#JsonProperty("bar")
public Bar getBar() {
return bar;
}
#JsonProperty("bar")
public void setBar(Bar bar) {
this.bar = bar;
}
}
test.json
This is the json file I am trying to deserialize.
Is this is the correct way to refer to a object/json reference?
{
"bar": {"$ref": "/bar.json"}
}
Deserializer.java
ObjectMapper objectMapper = new ObjectMapper();
//load class
URL url = Deserializer.class.getClassLoader().getResource("test.json");
//deserialize
objectMapper.readValue(url, Foo.class);
the result creates a Foo pojo with additional property of "bar": ""$ref": "/bar.json"" rather than deserializing it. Do I need to implement the deserialize interface and manually deserialize the node?
Traditionally in Comp Sc. this problem is solved using what is known as "Pointer Swizzling".
This means that If you have an Object A that contains a reference to B and you want to serialize this structure (and then deserialize it), you would need to "unswizzle" the pointer to B to a "name" (an identifier that uniquely identifies the instance B) , write it to disk. When deserializing, you would then take that name, find the instance that it points to (B) and "swizzle" the name back to a proper pointer to B.
Now, in Java pointers are called references but it's the same.
Here's an example to illustrate:
originalA = { "id":"id_a", "ref_to_b": originalB}
originalB = { "id":"id_b" }
Applying unswizzling:
readyForSerializationA = { "id":"id_a", "ref_to_b": "id_b"}
readyForSerializationB = { "id": "id_b" }
followed by writing to store/reading back from store.
Applying swizzling:
deserializedB = { "id":"id_b" }
deserializedA = { "id": "id_a", "ref_to_b": deserializedB}
One possible way to do it for your case,is to deserialize all objects first, put them into an HashMap and in a second pass, look up the ObjectReference(s) from the various ObjectID(s) that you have in your JSON (swizzling).
Some further reading: https://en.wikipedia.org/wiki/Pointer_swizzling
You need to store {"$ref": "/bar.json"} this as a Map. That's the simplest way to store it.
Example:
public class Foo {
#JsonProperty("bar")
private Map<String, Bar> bar;
#JsonProperty("bar")
public Map<String, Bar> getBar() {
return bar.get("$ref");
}
#JsonProperty("bar")
public void setBar(Map<String, Bar> bar) {
this.bar = bar;
}
}
Only then it will get the value of $ref in Bar object. Otherwise, the data will be in incorrect format and Bar object will take the entire bar value into it.
I'd like to go further on what this question was about, I've been roaming SO for a solid hour now without finding anything.
Basically, what I'm trying to do is having a property properly instanciated through Jackson internal reflection algorithm during deserialization but having this same property not serialized when it comes to serialization.
I know about #JsonIgnore and #JsonIgnoreProperties but apparently I can't seem to use them right : either my property is correctly deserialized when I feed Jackson a proper map of properties but it also appears in the serialized results, either (when using #JsonIgnore) it is not serialized (which is wanted) but also not deserialized (not wanted).
Example :
public class Foo {
/* This is the property I want to be instanciated by Jackson upon deserialization
* but not serialized upon serialization
*/
private final Object bar = null;
public Object getBar() {
return bar;
}
}
To make things worse, as you can see, the property is final (this is why I'm keen on using Jackson reflection ability upon Foo instanciation through deserialization). I've read on potential solution about annotating the setter and the getter differently but I'd like to keep this property final if possible. If not possible, I'd settle for a non-final property.
I would appreciate answers not suggesting custom serializer/deserializer, my code base is currently free of such and if the solution could be of minimal impact, that would be perfect. Again, I'm no Jackson expert so if what I'm asking is not possible I'll obviously accept alternative answers.
I've also read this thread on github but none of the suggested ways of implementation have actually been implemented at the moment.
Thanks
EDIT : to make things clearer
public class Foo {
private final String bar = null;
public String getBar() {
return bar;
}
#Override
public String toString() {
return bar;
}
}
public void testMethod() throws IOException {
String json = "{\"bar\":\"Value\"}";
ObjectMapper mapper = new ObjectMapper();
Foo foo = mapper.readValue(json, Foo.class);
System.out.println(foo); // should have a bar property set to "Value"
System.out.println(mapper.writeValueAsString(foo)); // should return an empty JSON object
}
I am not sure whether it is elegant solution but you can use MixIn feature. You have to create new interface which could look like below:
interface FooMixIn {
#JsonIgnore
String getBar();
}
Assume that your POJO looks like this:
class Foo {
private final String bar = null;
public String getBar() {
return bar;
}
#Override
public String toString() {
return bar;
}
}
Now you have to tell Jackson that you want to ignore this property:
String json = "{\"bar\":\"Value\"}";
System.out.println(json);
ObjectMapper deserializeMapper = new ObjectMapper();
deserializeMapper.addMixInAnnotations(Foo.class, FooMixIn.class);
System.out.println(deserializeMapper.readValue(json, Foo.class));
Above example prints:
{"bar":"Value"}
null
Without deserializeMapper.addMixInAnnotations(Foo.class, FooMixIn.class); line above program prints:
{"bar":"Value"}
Value
EDIT 1
If you want to achieve result like you showed you have to create two ObjectMappers and customize them. See below example:
String json = "{\"bar\":\"Value\"}";
ObjectMapper deserializerMapper = new ObjectMapper();
Foo foo = deserializerMapper.readValue(json, Foo.class);
System.out.println("Foo object: " + foo);
ObjectMapper serializerMapper = new ObjectMapper();
serializerMapper.disable(SerializationFeature.FAIL_ON_EMPTY_BEANS);
serializerMapper.addMixInAnnotations(Foo.class, FooMixIn.class);
System.out.println("JSON: " + serializerMapper.writeValueAsString(foo));
For serialization you have to use one instance and for deserialization you have to use another instance.
Starting with Jackson 2.6, a property can be marked as read- or write-only. It's simpler than hacking the annotations on both accessors (for non-final fields) and keeps all the information in one place. It's important to note that a final field is considered writable by default by Jackson.
However, it's not enough for a final field to allow deserialization, because you can't have a setter on that field: it needs to be set via the constructor, either directly or using a builder or another type that can be deserialized by Jackson. When using the constructor with the properties as parameters, you need to specify which parameter corresponds to which property, using #JsonProperty:
public class Foo {
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private final String bar;
public Foo(#JsonProperty("bar") String bar) {
this.bar = bar;
}
public String getBar() {
return prop;
}
}