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.
Related
In my DTO class, there could be fields which might have same #JsonAlias values, but this seems not working with Jackson ObjectMapper.
It seems that ObjectMapper only works for the first occurrence of #JsonAlias and it don't work for the rest #JsonAlias which has same value. I have tried to create an example below for the reference and in that example, Person class has the two fields with name #JsonAlias value.
Code snippet:
import com.fasterxml.jackson.annotation.JsonAlias;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.HashMap;
import java.util.Map;
public class Test {
public static void main(String[] args) {
Map<String, String> values = new HashMap<>();
values.put("name", "TEST NAME");
Person person = new ObjectMapper().convertValue(values, Person.class);
System.out.println("name1: " + person.getName1());
System.out.println("name2: " + person.getName2());
}
static class Person {
#JsonAlias("name")
String name1;
#JsonAlias("name")
String name2;
public String getName1() {
return name1;
}
public void setName1(String name1) {
this.name1 = name1;
}
public String getName2() {
return name2;
}
public void setName2(String name2) {
this.name2 = name2;
}
}
}
Output:
name1: TEST NAME
name2: null
In the above output. I was expecting the "TEST NAME" for the name2 variable in Person class.
Is there any configuration in Jackson ObjectMapper which will help me to achieve this?
Jackson version - jackson-databind-2.11.4.jar
Solution 1, #JsonGetter #JsonSetter point out to the custom getter\setter
We can use #JsonGetter("name") and #JsonSetter("name") annotations to map one JSON property value to the setter which has handling two properties
public class Person {
#JsonIgnore
private String name1;
#JsonIgnore
private String name2;
#JsonSetter("name")
public void setName(String name) {
this.name1 = name;
this.name2 = name;
}
#JsonGetter("name")
public String getName1() {
return name1;
}
public void setName1(String name1) {
this.name1 = name1;
}
public String getName2() {
return name2;
}
public void setName2(String name2) {
this.name2 = name2;
}
}
Solution 2, #JsonAlias with the specific setter
We can ignore one field, the second mark with an alias, and add a custom setName setter that maps one JSON value to several fields.
public class Person {
#JsonAlias("name")
private String name1;
#JsonIgnore
private String name2;
public String getName1() {
return name1;
}
public void setName1(String name1) {
this.name1 = name1;
}
public String getName2() {
return name2;
}
public void setName2(String name1) {
this.name1 = name1;
}
public void setName(String name) {
this.name1 = name;
this.name2 = name;
}
}
Or just add a custom setName setter to your original code
public class Person {
#JsonAlias("name")
String name1;
#JsonAlias("name")
String name2;
public String getName1() {
return name1;
}
public void setName1(String name1) {
this.name1 = name1;
}
public String getName2() {
return name2;
}
public void setName2(String name1) {
this.name1 = name1;
}
public void setName(String name) {
this.name1 = name;
this.name2 = name;
}
}
Solution 3, #JsonProperty with the specific setter
The same like Solution 2, but instead use #JsonProperty("name")
public class Person {
#JsonProperty("name")
private String name1;
#JsonIgnore
private String name2;
public String getName1() {
return name1;
}
#JsonIgnore
public void setName1(String name1) {
this.name1 = name1;
}
public String getName2() {
return name2;
}
public void setName2(String name1) {
this.name1 = name1;
}
public void setName(String name) {
this.name1 = name;
this.name2 = name;
}
}
UPDATE:
In case you do not have the ability to modify DTO object, custom deserializer resolve the problem.
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.deser.std.StdDeserializer;
import java.io.IOException;
public class PersonDeserializer extends StdDeserializer<Person> {
public PersonDeserializer() {
this(null);
}
public PersonDeserializer(Class<?> vc) {
super(vc);
}
#Override
public Person deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException {
Person person = new Person();
JsonNode node = jp.getCodec().readTree(jp);
String name = node.get("name").asText();
person.setName1(name);
person.setName2(name);
return person;
}
}
Apply deserializer to the DTO object:
#JsonDeserialize(using = PersonDeserializer.class)
public class Person {
private String name1;
private String name2;
//getters and setters
}
OR register custom deserializer on ObjectMapper
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Person.class, new PersonDeserializer());
mapper.registerModule(module);
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 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
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 new to Java and Jackson and a lot of other technologies which I try to use, so I'd appreciate a detailed answer.
Is there a way to prevent one or more fields from being serialized using Jackson into a JSON String_like format, but without using any kind of JSON annotations?
Something like: mapper.getSerializationConfig().something(ignore("displayname")) if you know what I mean.
My object is an instance of a class that extends another one, and implements one interface also so on, so the fields come from an hierarchy of classes.
I need the JSON representation for that object but containing only certain fields, so I can send that JSON in a mock request through a POST method.
I'm using Jackson 2.2.2.
If you can't change your classes you can create new abstract class/interface with methods with #JsonIgnore annotation. In this class/interface you can define methods which ObjectMapper should skip during serialization/deserialization process.
Please, see below example:
import java.io.IOException;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonProgram {
public static void main(String[] args) throws IOException {
Person person = new Person();
person.setId(1L);
person.setName("Max");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn(Person.class, PersonMixIn.class);
System.out.println(objectMapper.writeValueAsString(person));
}
}
abstract class Entity {
private Long id;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
}
interface Namamble {
String getName();
}
class Person extends Entity implements Namamble {
private String name;
#Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
interface PersonMixIn {
#JsonIgnore
String getName();
}
EDIT - answer for the comments
You can create such mixin interface:
public static interface UserInformationMixIn {
#JsonIgnore
String getField3();
}
and configure ObjectMapper in this way:
objectMapper.addMixInAnnotations(UserInformation.class, UserInformationMixIn.class);
In version 2.5 method addMixInAnnotations was deprecated and addMixIn should be used:
objectMapper.addMixIn(UserInformation.class, UserInformationMixIn.class);
Full example source code:
import java.io.IOException;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.ObjectMapper;
public class JacksonProgram {
public static void main(String[] args) throws IOException {
UserInformation userInformation = new UserInformation();
userInformation.setField3("field3");
userInformation.setField4("field4");
userInformation.setField5("field5");
User user = new User();
user.setField1(userInformation);
user.setField2("field2");
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.addMixIn(UserInformation.class, UserInformationMixIn.class);
objectMapper.addMixIn(User.class, UserInformationMixIn.class);
System.out.println(objectMapper.writeValueAsString(user));
}
public static abstract class Someclass {
String field5;
public String getField5() {
return field5;
}
public void setField5(String field5) {
this.field5 = field5;
}
}
public static class UserInformation extends Someclass {
String field3;
String field4;
public String getField3() {
return field3;
}
public void setField3(String field3) {
this.field3 = field3;
}
public String getField4() {
return field4;
}
public void setField4(String field4) {
this.field4 = field4;
}
}
public static class User {
UserInformation field1;
String field2;
public UserInformation getField1() {
return field1;
}
public void setField1(UserInformation field1) {
this.field1 = field1;
}
public String getField2() {
return field2;
}
public void setField2(String field2) {
this.field2 = field2;
}
}
public static interface UserInformationMixIn {
#JsonIgnore
String getField3();
#JsonIgnore
String getField2();
#JsonIgnore
String getField5();
}
}
Helpful link:
How can I tell jackson to ignore a property for which I don't have
control over the source code?