i am trying to map certain json fields to a class instance variable.
My sample Person class looks like:
public class Person {
private String name;
private Address address;
//many more fields
//getters and setters
}
The sample Address class is:
public class Address {
private String street;
private String city;
//many more fields
// getters and setters
}
The json object to be deserialized to my Person class doesn't contain "address" field. It looks like:
{
"name":"Alexander",
"street":"abc 12",
"city":"London"
}
Is there a way to deserialize the json to the Person pojo where the Address fields are also mapped properly?
I have used a custom Address deserializer as mentioned in so many posts here. However, it's not being called as the Json object doesn't contain "address" field.
I had resolved this problem by mapping each field manually using JsonNode, however in my real project, it's not a nice solution.
Is there any work around for such problem using jackson?
Plus if this question has been asked before then apologies on my behalf as as i have intensively searched for the solution and might have not seen it yet. .
#JsonUnwrapped annotation was introduced for this problem. Model:
class Person {
private String name;
#JsonUnwrapped
private Address address;
// getters, setters, toString
}
class Address {
private String street;
private String city;
// getters, setters, toString
}
Usage:
ObjectMapper mapper = new ObjectMapper();
String json = "{\"name\":\"Alexander\",\"street\":\"abc 12\",\"city\":\"London\"}";
System.out.println(mapper.readValue(json, Person.class));
Prints:
Person{name='Alexander', address=Address{street='abc 12', city='London'}}
For more info read:
Jackson Annotation Examples
Annotation Type JsonUnwrapped
Jackson JSON - Using #JsonUnwrapped to serialize/deserialize properties as flattening data structure
I don't think you really have a deserialization problem here but rather a general Java problem: how to make sure the address field always contains a value. All you need to do is either assign address to a default value in the Person constructor, or generate and assign a default value for address in the Person.getAddress method.
I understood your problem so that it is about flat Json that has all Address fields at the same level as Person. Even if it is not exactly so this might help you. JsonDeserializer will do fine but you need to apply it to Person because it is the level where all the fields are.
So like this:
public class CustomDeserializer extends JsonDeserializer<Person> {
// need to use separate ObjectMapper to prevent recursion
// this om will not be registered with this custom deserializer
private final ObjectMapper om;
{
om = new ObjectMapper();
// this is needed because flat json contains unknown fields
// for both types.
om.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}
#Override
public Person deserialize(JsonParser parser, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// make a string of json tree so not any particular object
String json = om.readTree(parser).toString();
// deserialize it as person (ignoring unknown fields)
Person person = om.readValue(json, Person.class);
// set address deserializing it from teh same string, same manner
person.setAddress(om.readValue(json, Address.class));
return person;
}
}
Of course this is not the only way and might not have the best performance but it is only about how you do the deserialization in your custom deserializer. If your Person & Address objects are havin like 10 fields each using this should not be a problem.
Update
I think that in your case - based on your example data - MichaĆ Ziober's
answer might be the best but if you need any more complex handling than plain unwrapping for your data you just need to deserialize Person class somehow like I presented.
Related
I have a class with the following fields and their respective getters, plus an additional method getTotalBalance for which I don't have any field but a custom implementation.
public class demo{
private String balance;
private String blockedBalace;
private String futureBalance;
private String availableBalance;
//getters for previous fields
public String getTotalBalance(){
//something..
}
When I serialize an object of this class I get the following JSON output.
{
"balance": "12.30",
"blockedBalance":"23.45",
"futureBalance" :"56.22",
"availableBalance" :"12.30",
"totalBalance" : "34.11"
}
Even if I didn't declare a field for totalBalance, I've got this serialized in the end. How is it possible?
Jackson by default uses the getters for serializing and setters for deserializing.
You can use #JsonIgnore over your getter method to ignore it, OR you can configure your object mapper to use the fields only for serialization/des:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
Jackson doesn't (by default) care about fields. It will simply serialize everything provided by getters and deserialize everything with a matching setter. What those getters/setters do is of no consequence.
Mind you though, that every little thing about Jackson can be deeply customized and configured, so I'm only talking about the default setup.
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 have a server that returns a json string:
{"pId": "ChIJ2Vn0h5wOlR4RsOSteUYYM6g"}
Now, I can use jackson to deserialize it into an object with the variable called pId, but I don't want the variable to be called pId, I would rather deserialize it to placeId.
Current object in android java:
public class Place {
private String pId;
}
What I want the object to look like:
public class Place {
private String placeId;
}
If I change the object's variable to placeId, jackson will not be able to deserialize the JSON as the property names no longer matches.
Is there a jackson annotation I can used to map the "placeId" variable in the java object to the JSON string variable "pId" returned back from the server?
Use #JsonProperty annotation:
public class Place {
#JsonProperty("pId")
private String placeId;
}
For more information you can see the related javadoc.
I'm calling a rest service that returns a json object. I'm trying to deserialize the responses to my Java Beans using Jackson and data-binding.
The example Json is something like this:
{
detail1: { property1:value1, property2:value2},
detail2: { property1:value1, property2:value2},
otherObject: {prop3:value1, prop4:[val1, val2, val3]}
}
Essentially, detail1 and detail2 are of the same structure, and thus can be represented by a single class type, whereas OtherObject is of another type.
Currently, I've set up my classes as follows (this is the structure I would prefer):
class ServiceResponse {
private Map<String, Detail> detailMap;
private OtherObject otherObject;
// getters and setters
}
class Detail {
private String property1;
private String property2;
// getters and setters
}
class OtherObject {
private String prop3;
private List<String> prop4;
// getters and setters
}
Then, just do:
String response = <call service and get json response>
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(response, ServiceResponse.class)
The problem is I'm getting lost reading through the documentation about how to configure the mappings and annotations correctly to get the structure that I want. I'd like detail1, detail2 to create Detail classes, and otherObject to create an OtherObject class.
However, I also want the detail classes to be stored in a map, so that they can be easily distinguished and retrieved, and also the fact that the service in in the future will return detail3, detail4, etc. (i.e., the Map in ServiceResponse would look like
"{detail1:Detail object, detail2:Detail object, ...}).
How should these classes be annotated? Or, perhaps there's a better way to structure my classes to fit this JSON model? Appreciate any help.
Simply use #JsonAnySetter on a 2-args method in ServiceResponse, like so:
#JsonAnySetter
public void anySet(String key, Detail value) {
detailMap.put(key, value);
}
Mind you that you can only have one "property" with #JsonAnySetter as it's a fallback for unknown properties. Note that the javadocs of JsonAnySetter is incorrect, as it states that it should be applied to 1-arg methods; you can always open a minor bug in Jackson ;)
I have a simple POJO like
public class Employee
{
int level;
int salary;
Map<String, String> details; // HashMap
}
A serialized object of this class looks like
{"level":1,"salary":30000, "details":{"address":"ADDRESS", "phone":"12345678"}}
Assuming the above JSON string is stored in a Java String variable called json,
when deserializing it via the following Jackson statement
Employee employee = new ObjectMapper().readValue(json, Employee.class);
the object is properly created, no exception occurs, the fields "level" and "salary" are correctly populated, but the "details" field (originally a HashMap) is always null.
How can I correctly deserialize it?
Jackson correctly deserialized the details attribute with the version of Jackson that I have in my machine-1.8.1. Can you confirm that you have accessors created for Employee class, if not that could be the reason.