问题
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. .
回答1:
@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
回答2:
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.
回答3:
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.
来源:https://stackoverflow.com/questions/54370637/wrapping-json-fields-into-instance-variable-of-a-pojo