I have a need to have two different ObjectMapper in the application.
Pojo I am working with:
public class Student {
private String name;
private Integer age;
#HideThisField
private String grade;
// getters & setters..
}
One is the out of the box configuration based ObjectMapper as below:
#Bean("objectMapper")
public ObjectMapper getRegularObjectMapper() {
//With some configurations
return new ObjectMapper();
}
I need another ObjectMapper that while serializing ignores a few fields for all objects based on an annotation on a field.
#Bean("customObjectMapper")
public ObjectMapper getCustomObjectMapper() {
// This is where i want to ignore the fields with #HideThisField
return new ObjectMapper();
}
Output of the two mappers:
objectMapper.writeValuesAsString(someStudent) prints:
{"name": ""student1", age: 10, "grade": "A+"}
customObjectMapper.writeValuesAsString(someStudent) prints:
{"name": ""student1", age: 10}
JacksonAnnotationIntrospector handles standard Jackson annotations. Overriding the hasIgnoreMarker method, you can make it work according to your own annotation.
import com.fasterxml.jackson.core.*;
import com.fasterxml.jackson.databind.*;
import com.fasterxml.jackson.databind.introspect.*;
import java.lang.annotation.*;
public class StudentExample {
public static void main(String[] args) throws JsonProcessingException {
Student student = new Student();
student.setName("Student 1");
student.setAge(10);
student.setGrade("A+");
String st1 = getRegularObjectMapper().writeValueAsString(student);
String st2 = getCustomObjectMapper().writeValueAsString(student);
System.out.println(st1);
System.out.println(st2);
}
public static ObjectMapper getRegularObjectMapper() {
return new ObjectMapper();
}
public static ObjectMapper getCustomObjectMapper() {
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
objectMapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector() {
#Override
public boolean hasIgnoreMarker(AnnotatedMember m) {
if (_findAnnotation(m, HideThisField.class) != null)
return true;
return false;
}
});
return objectMapper;
}
}
class Student {
private String name;
private Integer age;
#HideThisField
private String grade;
public Student() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getGrade() {
return grade;
}
public void setGrade(String grade) {
this.grade = grade;
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.FIELD)
#interface HideThisField {}
The console output is:
{"name":"Student 1","age":10,"grade":"A+"}
{"name":"Student 1","age":10}
getCustomObjectMapper() don't skips JsonIgnore annotations because you override the standard, if you want, you need to add this to the if block.
Good morning guys!
I have a JSON strings that looks like:
{
"StatusCode":0,
"Message":null,
"ExecutionTime":0,
"ResponseData":[
{"Name":"name1","SiteId":"1234","Type":"Type1","X":"1234567","Y":"123456"},
{"Name":"Name2","SiteId":"2134","Type":"Type2","X":"1234567","Y":"1234567"},
{"Name":"Name3","SiteId":"3241","Type":"Type3","X":"1234567","Y":"1234567"},
{"Name":"Name4","SiteId":"4123","Type":"Type4","X":"123456","Y":"123456"}
]
}
I want to create an object where I can retrieve the Xand Y values.
I've been trying to use Jackson to serialize the JSON string, without success. I've created two extra classes for Jackson to use. One class for the top layer, StatusCode, Message, ExecutionTime and ResponseData which looks like
public class PL {
private Long statusCode;
private String executionTime;
private String message;
private ResponseData responseData;
public PL(){
}
public void setStatusCode(Long statusCode){
this.statusCode = statusCode;
}
public Long getStatusCode(){
return this.statusCode;
}
public void setExecutionTime(String executionTime){
this.executionTime = executionTime;
}
public String getExecutionTime(){
return this.executionTime;
}
public void setMessage(String message){
this.message = message;
}
public String getMessage(){
return this.message;
}
public void setResponseData(ResponseData responseData){
this.responseData = responseData;
}
public ResponseData getResponseData(){
return this.responseData;
}
}
Where ReponseData is returned as an object, and then I have another class for serializing ResponseData which looks like
public class ResponseData {
private String name;
private String siteId;
private String type;
private String x;
private String y;
public ResponseData(){
}
public void setName(String name){
this.name = name;
}
public String getName(){
return this.name;
}
public void setSiteId(String siteId){
this.siteId = siteId;
}
public String getSiteId(){
return this.siteId;
}
public void setType(String type){
this.type = type;
}
public String setType(){
return this.type;
}
public void setX(String x){
this.x = x;
}
public String getX(){
return this.x;
}
public void setY(String y){
this.y = y;
}
public String getY(){
return this.y;
}
}
I then create an ObjectMapper with
private final static ObjectMapper mapper = new ObjectMapper();
and try to so read the values with
ResponseData e = mapper.readValue(result.toString(), ResponseData.class);
and end up with the exception
com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "StatusCode" (class MyClass.ResponseData), not marked as ignorable (5 known properties: "x", "y", "siteId", "name", "type"])
as if it can't parse the first entry, StatusMessage. Even if I remove the second class and only try to parse the first four entries where i return ResponseData as a String I still get the same exception.
To start with, in PL you should have a List<ResponseData> not a simple ResponseData attribute. As you can see, in the JSON, ResponseData is an array "ResponseData":[...] so it will be deserialized as a List. Each element of the list will be a ResponseData object as you defined it.
Then you have a case issue, you have upper cases in the JSON that you don't have in your class attributes. You can use the #JsonProperty (See API) annotation to overcome the problem, this way:
class PL {
#JsonProperty("StatusCode")
private Long statusCode;
#JsonProperty("ExecutionTime")
private String executionTime;
#JsonProperty("Message")
private String message;
#JsonProperty("ResponseData")
private List<ResponseData> responseDatas;
public PL(){
}
// getters/Setters
}
class ResponseData {
#JsonProperty("Name")
private String name;
#JsonProperty("SiteId")
private String siteId;
#JsonProperty("Type")
private String type;
#JsonProperty("X")
private String x;
#JsonProperty("Y")
private String y;
public ResponseData(){
}
// getters/Setters
}
Then read your JSON as a PL object, like this:
ObjectMapper mapper = new ObjectMapper();
PL pl = mapper.readValue(json, PL.class);
for(ResponseData rd : pl.getResponseDatas()) {
System.out.println(rd.getX());
System.out.println(rd.getY());
}
This outputs:
1234567
123456
1234567
1234567
1234567
1234567
123456
123456
It is fairly straightforward. Define your response structure using composition of classes. It is unfortunate to use capitalised fields in JSON, which out-of-the-box requires capitalised field names in the Java DTO. Still those can be easily mapped to conventional low-case names either by using the ACCEPT_CASE_INSENSITIVE_PROPERTIES modifier on the ObjectMapper or by annotating fields with corresponding names. I prefer a property on the ObjectMapper as it keeps the DTO independent of the serialisation code and this technique is used in the test below (the test is green):
import com.fasterxml.jackson.databind.MapperFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
public class TestDeserialization50386188 {
public static class Response {
public static class ResponseDataType {
public String name;
public String siteId;
public String type;
public long x;
public long y;
}
public int statusCode;
public String message;
public long executionTime;
public List<ResponseDataType> ResponseData = new ArrayList<>();
}
private static final String data = "{\"StatusCode\":0,\"Message\":null,\"ExecutionTime\":0,\"ResponseData\":[{\"Name\":\"name1\",\"SiteId\":\"1234\",\"Type\":\"Type1\",\"X\":\"1234567\",\"Y\":\"123456\"},{\"Name\":\"Name2\",\"SiteId\":\"2134\",\"Type\":\"Type2\",\"X\":\"1234567\",\"Y\":\"1234567\"},{\"Name\":\"Name3\",\"SiteId\":\"3241\",\"Type\":\"Type3\",\"X\":\"1234567\",\"Y\":\"1234567\"},{\"Name\":\"Name4\",\"SiteId\":\"4123\",\"Type\":\"Type4\",\"X\":\"123456\",\"Y\":\"123456\"}]}";
#Test
public void deserialize_response_withJackson_ok() throws IOException {
ObjectMapper mapper = new ObjectMapper()
.configure(MapperFeature.ACCEPT_CASE_INSENSITIVE_PROPERTIES, true);
Response response = mapper.readValue(data, Response.class);
assertEquals(4, response.ResponseData.size());
assertEquals(1234567, response.ResponseData.get(2).x);
assertEquals(1234567, response.ResponseData.get(2).y);
}
}
You fill find the project with the executable test on this dedicated GitHub repo.
The "Clean Code" book by Uncle Bob does not really recommend the overuse of getters and setters so common in Java for DTOs, which a Response class is. Still you can replace all public fields with getter/setter pairs if you like but the clarity will suffer with no obvious gain on quality.
Use List to receive arrays.
private Long statusCode;
private String executionTime;
private String message;
public List<ResponseDataType> ResponseData
and it will do everything automatically.
I have a class Student with some fields. I wanted to give custom names for the JSON fields that get returned.
public class Student {
#JsonProperty("name")
private String mName;
#JsonProperty("DOB")
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd")
private Date mBirthDate;
#JsonProperty("SSN")
private String mSocialSecurityNumber;
public Student() {
}
public Student(String mName, Date mBirthDate, String mSocialSecurityNumber) {
this.mName = mName;
this.mBirthDate = mBirthDate;
this.mSocialSecurityNumber = mSocialSecurityNumber;
}
public String getName() {
return mName;
}
public void setName(String mName) {
this.mName = mName;
}
public Date getBirthDate() {
return mBirthDate;
}
public void setBirthDate(Date mBirthDate) {
this.mBirthDate = mBirthDate;
}
public String getSocialSecurityNumber() {
return mSocialSecurityNumber;
}
public void setSocialSecurityNumber(String mSocialSecurityNumber) {
this.mSocialSecurityNumber = mSocialSecurityNumber;
}
}
My JSON output has both the raw field name (based on the getter name, e.g. getSocialSecurityNumber()), as well as the name specified in my #JsonProperty attributes.
It seems like if I move the #JsonProperty attributes to the getters, then I don't get the doubleup of the fields. Is there not a way I can do this by just having the annotations on the fields, which I feel is a little cleaner?
Configure the ObjectMapper to consider only the fields:
ObjectMapper mapper = new ObjectMapper();
mapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
mapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
In Spring Boot you can use Jackson2ObjectMapperBuilder to configure the ObjectMapper:
#Bean
public Jackson2ObjectMapperBuilder objectMapperBuilder() {
return new Jackson2ObjectMapperBuilder() {
#Override
public void configure(ObjectMapper objectMapper) {
super.configure(objectMapper);
objectMapper.setVisibility(PropertyAccessor.ALL, Visibility.NONE);
objectMapper.setVisibility(PropertyAccessor.FIELD, Visibility.ANY);
}
};
}
I need to create a Map from java bean such that the key is prefixed with name of the java bean variable. I am using jackson for this. Example given below:
public class Address{
String city;
String state;
//setters and getters
}
Address address = new Address();
address.setCity("myCity");
address.setState("myState");
I am creating map using following:
ObjectMapper objectMapper = new ObjectMapper();
Map map = objectMapper.convertValue(address, HashMap.class);
Which gives me following output:
{"city":"myCity", "state":"myState"}
I need to add class variable name to the key as shown below:
{"address.city":"myCity", "address.state":"myState"}
How do I achieve that?
If you have jackson-annotations enabled:
public class Address{
#JsonProperty("address.city")
String city;
#JsonProperty("address.state")
String state;
//setters and getters
}
read more about it here: https://github.com/FasterXML/jackson-annotations
It is possible to customise bean serialization by registering a BeanSerializerModifier. This specifically supports renaming properties by applying a NameTransformer to each BeanPropertyWriter.
#Test
public void prepend_class_name_to_property_keys() throws Exception {
ObjectMapper mapper = new ObjectMapper();
Function<Class<?>, String> classPrefix = clazz -> clazz.getSimpleName().toLowerCase() + ".";
mapper.registerModule(new Module() {
#Override
public String getModuleName() {
return "Example";
}
#Override
public Version version() {
return Version.unknownVersion();
}
#Override
public void setupModule(SetupContext context) {
context.addBeanSerializerModifier(new BeanSerializerModifier() {
#Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config,
BeanDescription beanDesc, List<BeanPropertyWriter> beanProperties) {
String prefix = classPrefix.apply(beanDesc.getBeanClass());
return beanProperties.stream().map(prop -> prop.rename(new NameTransformer() {
#Override
public String transform(String name) {
return prefix + name;
}
#Override
public String reverse(String transformed) {
return transformed.substring(prefix.length());
}
})).collect(toList());
}
});
}
});
assertThat(mapper.writeValueAsString(new Address("somewhere", "someplace")),
equivalentTo("{ 'address.line1' : 'somewhere', 'address.line2' : 'someplace'}"));
}
public static final class Address {
public final String line1;
public final String line2;
public Address(String line1, String line2) {
this.line1 = line1;
this.line2 = line2;
}
}
I'm trying to serialze part of a JSON string into an object. The JSON string looks as follows:
{"error":null,"excludeFields":null,"message":null,"success":{"user":{"name":null,"organizationId":100,"username":"nl4321"}}}
I only need the user-part of the JSON string, which corresponds to an object of the following class:
public class UserForm implements Serializable {
private static final long serialVersionUID = -5033294929007794646L;
private String username;
private String name;
#Getter #Setter
private int organizationId;
public UserForm() {
}
public UserForm(User user) {
if (user != null) {
this.username = user.getUsername();
this.name = user.getName();
this.organizationId = user.getDefaultOrganisationId();
}
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
The way I'm currently trying to deserialize it, is as follows:
private UserForm deserializeToJsonResponse(String bodyContent) throws IOException {
JSONDeserializer<UserForm> jsonDeserializer = new JSONDeserializer<UserForm>();
return jsonDeserializer.use("values.success.user", UserForm.class).deserialize(bodyContent, UserForm.class);
}
The use method is provided with both a path and a class, but no matter what path I try, the contents of the UserForm are null after deserialization. I've tried: "user", "success.user", "values.success.user".
Does anyone have an idea what I'm doing wrong and how I can fix it?
I realize there are other solutions out there, like Jackson, but this is part of a large codebase that already uses FlexJSON a lot. Background: I'm trying to write unit tests for an API that's part of the project.