#JsonProperty on class fields - getting duplicate fields in JSON - java

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);
}
};
}

Related

How can I exclude fields with custom annotation in Spring Boot ObjectMapper

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.

#JsonFormat not working in nested object

I have a very simple bean:
public class StatusBean {
private String name;
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="MM-dd-yyyy")
private Date startDate;
#JsonFormat(shape=JsonFormat.Shape.STRING, pattern="MM-dd-yyyy")
private Date endDate;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getEndDate() {
return endDate;
}
public void setEndDate(Date endDate) {
this.endDate = endDate;
}
}
And I wrap it in another bean that I use to wrap objects for nice json formatting with messages and stuff:
public class ResponseBean {
private boolean success = false;
private String message;
private Object data;
public boolean isSuccess() {
return success;
}
public void setSuccess(boolean success) {
this.success = success;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public Object getData() {
return data;
}
public void setData(Object data) {
this.data = data;
}
}
In my controller, I set the Status bean inside the response bean with a setData();
Spring serializes this out in JSON format, however the output for the date is not formatting. I am getting the standard "yyyy-MM-DD" format.
Am I doing something wrong? How do I get this to work?
I had the same issue and fixed simply adding #JsonSerialize(as = Date.class) before #JsonFormat(shape=JsonFormat.Shape.STRING, pattern="MM-dd-yyyy")
With #DateTimeFormat(pattern="dd/MM/yyyy") from
org.springframework.format.annotation.DateTimeFormat worked for me.
I have never tried it but the solution could be to add this annotation in your ResponseBean:
#JsonSerialize(as = StatusBean.class)
private Object data;
unfortunately your Object will become a StatusBean
Possibility not write object with ObjectMapper
new ObjectMapper().writeValueAsString(MyObject);
Full code example
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper objectMapper = new ObjectMapper();
System.out.println(objectMapper.writeValueAsString(new Foo(new java.util.Date())));
System.out.println(objectMapper.writeValueAsString(new Foo(new java.sql.Date(System.currentTimeMillis()))));
}
static class Foo {
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd/MM/yyyy", timezone="EST")
private Date birthdate;
public Foo() {
}
public Foo(Date birthdate) {
this.birthdate = birthdate;
}
public Date getBirthdate() {
return birthdate;
}
}

ObjectMapper SetDateFormat() is not working

I have used ObjectMapper to map a object to Json String.
I have date Fields in the object to format the date i have used the below code but it is not formatting as expected.
My Json String:
{"leaveRequestId":51,"reason":"xdvfsgf","leaveFromDate":"2016-07-13","leaveToDate":"2016-07-15","leaveTypeId":9,"statusId":1,"instanceId":"7527","createdBy":"pramod","createdOn":"2016-07-07","modifiedBy":null,"modifiedOn":null}
I am using the below code:
#RequestMapping(value="/getLeaveRequest", method = RequestMethod.GET)
#ResponseBody
public String getLeaveRequest( int leaveRequestId) throws Exception {
DAOFactory obj_daofactory=new DAOFactory();
ObjectMapper mapper = new ObjectMapper();
SimpleDateFormat df = new SimpleDateFormat("yyyy-MMM-dd");
mapper.setDateFormat(df);
LeaveRequest leaveRequest = obj_daofactory.getLeaveRequestDao().findByLeaveRequestId(leaveRequestId);
if(leaveRequest.getLeaveRequestId() == 0){
return "No data found";
} else {
System.out.println(leaveRequest.getLeaveFromDate().toString());
String jsonInString = mapper.writeValueAsString(leaveRequest);
System.out.println(jsonInString);
return jsonInString;
}
}
MY Expected OutPut:
{"leaveRequestId":45,"reason":"test","leaveFromDate":"2016-Jul-07","leaveToDate":"2016-Jul-08","leaveTypeId":9,"statusId":1,"instanceId":"test1","createdBy":"deepak.paul#muraai.com","createdOn":"2016-Jul-07","modifiedBy":"pramod","modifiedOn":"2016-Jul-08"}
Date must be in the "2016-Jul-07" format
LeaveRequest.java
import java.util.Date;
public class LeaveRequest {
private int leaveRequestId;
private String reason;
private Date leaveFromDate;
private Date leaveToDate;
private int leaveTypeId;
private int statusId;
private String instanceId;
private String createdBy;
private Date createdOn;
private String modifiedBy;
private Date modifiedOn;
public LeaveRequest() {
}
public LeaveRequest(int leaveRequestId, String reason, Date leaveFromDate, Date leaveToDate,int leaveTypeId,int statusId, String instanceId,
String createdBy, Date createdOn, String modifiedBy, Date modifiedOn) {
this.leaveRequestId=leaveRequestId;
this.reason=reason;
this.leaveFromDate=leaveFromDate;
this.leaveToDate=leaveToDate;
this.leaveTypeId=leaveTypeId;
this.statusId=statusId;
this.instanceId=instanceId;
this.createdBy=createdBy;
this.createdOn=createdOn;
this.modifiedBy=modifiedBy;
this.modifiedOn=modifiedOn;
}
public LeaveRequest(String reason, Date leaveFromDate, Date leaveToDate,int leaveTypeId,int statusId, String instanceId,
String createdBy, Date createdOn) {
this.reason=reason;
this.leaveFromDate=leaveFromDate;
this.leaveToDate=leaveToDate;
this.leaveToDate=leaveToDate;
this.leaveTypeId=leaveTypeId;
this.statusId=statusId;
this.instanceId=instanceId;
this.createdBy=createdBy;
this.createdOn=createdOn;
}
public int getLeaveRequestId() {
return leaveRequestId;
}
public void setLeaveRequestId(int leaveRequestId) {
this.leaveRequestId = leaveRequestId;
}
public String getReason() {
return reason;
}
public void setReason(String reason) {
this.reason = reason;
}
public Date getLeaveToDate() {
return leaveToDate;
}
public void setLeaveToDate(Date leaveToDate) {
this.leaveToDate = leaveToDate;
}
public Date getLeaveFromDate() {
return leaveFromDate;
}
public void setLeaveFromDate(Date leaveFromDate) {
this.leaveFromDate = leaveFromDate;
}
public int getStatusId() {
return statusId;
}
public void setStatusId(int statusId) {
this.statusId = statusId;
}
public int getLeaveTypeId() {
return leaveTypeId;
}
public void setLeaveTypeId(int leaveTypeId) {
this.leaveTypeId = leaveTypeId;
}
public String getCreatedBy() {
return createdBy;
}
public void setCreatedBy(String createdBy) {
this.createdBy = createdBy;
}
public Date getCreatedOn() {
return createdOn;
}
public void setCreatedOn(Date createdOn) {
this.createdOn = createdOn;
}
public String getModifiedBy() {
return modifiedBy;
}
public void setModifiedBy(String modifiedBy) {
this.modifiedBy = modifiedBy;
}
public Date getModifiedOn() {
return modifiedOn;
}
public void setModifiedOn(Date modifiedOn) {
this.modifiedOn = modifiedOn;
}
public String getInstanceId() {
return instanceId;
}
public void setInstanceId(String instanceId) {
this.instanceId = instanceId;
}
}
As you said you input is 2016-07-13 which is yyyy-MM-dd format. You have to read it using yyyy-MM-dd and while writing use yyyy-MMM-dd
ObjectMapper mapper = new ObjectMapper();
try {
mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd"));
LeaveRequest lrq = mapper.readValue("{\"leaveRequestId\":51,\"reason\":\"xdvfsgf\",\"leaveFromDate\":\"2016-07-13\",\"leaveToDate\":\"2016-07-15\",\"leaveTypeId\":9,\"statusId\":1,\"instanceId\":\"7527\",\"createdBy\":\"pramod\",\"createdOn\":\"2016-07-07\",\"modifiedBy\":null,\"modifiedOn\":null}", LeaveRequest.class);
System.out.println(mapper.setDateFormat(new SimpleDateFormat("yyyy-MMM-dd")).writeValueAsString(lrq));
} catch (IOException ex) {
Logger.getLogger(NewClass1.class.getName()).log(Level.SEVERE, null, ex);
}
I wrote this code to convert date to "yyyy-MM-dd" which ideally should have worked but it didn't
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setTimeZone(TimeZone.getDefault());
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(dateFormat);
myObj =objectMapper.readValue(file,MyObj.class)
Output was coming as - "paymentDate": "2019-08-18T23:00:00.000+0000", which isnt what I was expecting..
Changed this to Declaring ObjectMapper as a Bean and then inject ObjectMapper as a dependency
//Make sure the class is annotated with #Configuration or appropriate
#Bean
public ObjectMapper objectMapper() {
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setTimeZone(TimeZone.getDefault());
ObjectMapper mapper = new ObjectMapper();
mapper.setDateFormat(dateFormat);
return mapper;
}
and in-service class, just inject this as a dependency
#Autowired
private ObjectMapper objectMapper;
somemethod(){
myObj =objectMapper.readValue(file,MyObj.class)
}
Output was coming as -"paymentDate": "2019-08-19" -as expected
Serialization and deserialization will have to come into picture when you manipulate/modify the contents as per your requirement through ObjectMapper. You can refer more about this here
This topic has also been discussed here.

How to add #JsonIgnore annotated fields in serializing in Jackson ObectMapper

I need to add #JsonIgnore annotated fields while serializing an object by Jackson ObjectMapper. I know you may offer me to remove the #JsonIgnore annotation from my class, but I need they are ignorable in some part of my application. And in another part of my application I need to have those #JsonIgnore annotated fields in my json string.
You can define a SimpleBeanPropertyFilter and FilterProvider.
First annotate your class with custom filter like this:
#JsonFilter("firstFilter")
public class MyDtoWithFilter {
private String name;
private String anotherName;
private SecondDtoWithFilter dtoWith;
// get set ....
}
#JsonFilter("secondFilter")
public class SecondDtoWithFilter{
private long id;
private String secondName;
}
and this is how you will dynamically serialise your object.
ObjectMapper mapper = new ObjectMapper();
// Field that not to be serialised.
SimpleBeanPropertyFilter firstFilter = SimpleBeanPropertyFilter.serializeAllExcept("anotherName");
SimpleBeanPropertyFilter secondFilter = SimpleBeanPropertyFilter.serializeAllExcept("secondName");
FilterProvider filters = new SimpleFilterProvider().addFilter("firstFilter", firstFilter).addFilter("secondFilter", secondFilter);
MyDtoWithFilter dtoObject = new MyDtoWithFilter();
String dtoAsString = mapper.writer(filters).writeValueAsString(dtoObject);
I would suggest removing and re-adding them programmatically via reflection when your specific mapping is happening.
That suggests to me you have two different models with some common elements. I would reexamine your model.
public class MainProgram {
#JsonFilter("nameRemoveFilter")
public static class User{
private String name;
private String age;
private String password;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
public static void main(String[] args) {
ObjectMapper mapper = new ObjectMapper();
FilterProvider filters = new SimpleFilterProvider().addFilter("nameRemoveFilter",
SimpleBeanPropertyFilter.filterOutAllExcept("name","age"));
// and then serialize using that filter provider:
User user = new User();
try {
String json = mapper.writer(filters).writeValueAsString(user);
System.out.println(json);
} catch (JsonProcessingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
Works for Latest version of Jackson after 2.0

How do I serialize a java bean using jackson and customize map key name?

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;
}
}

Categories

Resources