Java Jackson ObjectMapper Inheritance issue [duplicate] - java

I have a class:
public class User extends Body {
private Integer userId;
private String userName;
private String emailId;
//getters and setters
}
I want to exclude Body class properties with Jackson mapper because I get an error.
ObjectMapper mapper = new ObjectMapper();
User user = new User;
String jsonString = mapper.writeValueAsString(user);
How I can convert object to JSON with excluding all extended or implementation class? I need convert only User class without Body
My super class has many public method like this:
public final Enumeration method(String email) {
throw new RuntimeException("Error");
}
public final Object method(String name) {
throw new RuntimeException("Error");
}

1. Using #JsonView annotation
Jackson library has #JsonView annotation which allows to provide different views of the serialized class.
You need to create a class describing different views like this:
public class Views {
public interface Base {} // view of Base class properties
public interface Child {} // view of Child class properties (i.e. User)
}
Then you mark up the fields/getters in the base Body class with #JsonView(Views.Base.class):
public class Body {
#JsonView(Views.Base.class)
private int foo;
#JsonView(Views.Base.class)
public String getBar() {
return "bar";
}
// other getters/setters
}
The User class can be marked at class level:
#JsonView(Views.Child.class)
public class User extends Body {
private Integer userId;
private String userName;
private String email;
// getters/setters
}
And when serializing with ObjectMapper you set its writer up to use specific view writerWithView:
ObjectMapper mapper = new ObjectMapper();//
User user = new User(1, "Jack", "jack#company.com");
String json = mapper.writerWithView(Views.Child.class).writeValueAsString(user);
System.out.println("custom view: " + json);
System.out.println("full view: " + mapper.writeValueAsString(user));
Output:
custom view: {"userId":1,"name":"Jack","email":"jack#company.com"}
full view: {"foo":0,"userId":1,"name":"Jack","email":"jack#company.com","bar":"bar"}
2. Using #JsonIgnoreProperties annotation
It is also possible to customize the view of the child class by ignoring its parent class' properties:
#JsonIgnoreProperties({"foo", "bar"})
public class User extends Body {
private Integer userId;
private String name;
private String email;
}
Then there's no need to configure the writer ObjectMapper instance:
System.out.println("base class fields ignored: " + mapper.writeValueAsString(user));
Output:
base class fields ignored: {"userId":1,"name":"Jack","email":"jack#company.com"}
3. Configure ObjectMapper to set custom JacksonAnnotationIntrospector
It is also possible to configure the ObjectMapper instance to set a custom annotation introspector to completely ignore properties belonging to the parent Body class:
// imports for Jackson v.2.x
// import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
// import com.fasterxml.jackson.databind.introspect.JacksonAnnotationIntrospector;
// imports for Jackson v.1.9
import org.codehaus.jackson.map.introspect.AnnotatedMember;
import org.codehaus.jackson.map.introspect.JacksonAnnotationIntrospector;
class IgnoreBodyClassIntrospector extends JacksonAnnotationIntrospector {
#Override
public boolean hasIgnoreMarker(final AnnotatedMember member) {
return member.getDeclaringClass() == Body.class || super.hasIgnoreMarker(member);
}
}
Configure ObjectMapper and serialize User without any code changes to Body and User:
ObjectMapper mapper = new ObjectMapper()
.setAnnotationIntrospector(new IgnoreBodyClassIntrospector());
User user = new User(3, "Nobody", "nobody#company.com");
System.out.println("no base class fields: " + mapper.writeValueAsString(user));
Output:
no base class fields: {"userId":3,"name":"Nobody","email":"nobody#company.com"}

Related

Jackson exlude extended class

I have class:
public class User implements Body {
private Integer userId;
private String userName;
private String emailId;
//getter and setter
}
I want with Jackson mapper exclude Body class because I get error
ObjectMapper mapper = new ObjectMapper();
User user = new User;
String jsonString = mapper.writeValueAsString(user);
How I can convert object to JSON with exclude all extended or implementation class? I need convert only User class without Body
My super class have many public method like this:
public final Enumeration method(String email) {
throw new RuntimeException("Error");
}
public final Object method(String name) {
throw new RuntimeException("Error");
}
Simply annotate the class you are serializing and list the properties to exclude.
#JsonIgnoreProperties({"bodyPropertyA", "bodyPropertyB", "bodyPropertyC"})
public class User implements Body {
private Integer userId;
private String userName;
private String emailId;
}
Try to add a filter to exclude parent fields. Here is a test:
public class JacksonTest {
#Test
public void excludeParent() throws JsonProcessingException {
ObjectMapper objectMapper= new ObjectMapper();
SimpleBeanPropertyFilter theFilter = SimpleBeanPropertyFilter
.serializeAllExcept("name","phone");
FilterProvider filters = new SimpleFilterProvider()
.addFilter("filterPerson", theFilter);
objectMapper.setFilterProvider(filters);
final User user = new User("email1",12);
user.setName("personName");
user.setPhone("12345");
System.out.println(objectMapper.writeValueAsString(user));
}
}
#Data
#AllArgsConstructor
#NoArgsConstructor
#JsonFilter("filterPerson")
class User extends Person{
String email;
int age;
}
#Data
#AllArgsConstructor
#NoArgsConstructor
class Person{
String name;
String phone;
}
console:
{"email":"email1","age":12}
Try this:
#JsonIgnore
public interface HiddenBody extends Body {
}
public class User implements HiddenBody {
//
}
The Liskov Substitution Principle says everything should still compile and execute OK.

Jackson - deserialize using Builder without annotation

Is it possible to use Jackson to deserialize a value class (final, no setters) that only has an all args constructor and a Builder? I can't use the JsonDeserialize and JsonPOJOBuilder since I am trying to deserialize a model defined in a client library, so I cannot add the annotations. Can I specify the builder to use another way?
You can try using MixIn.
I have created one sample for your use case:
Original class:
final class Sample {
final int id;
Sample(int id) {
this.id = id;
}
}
MixIn (provide non-args constructor with same args):
#JsonAutoDetect(fieldVisibility = Visibility.ANY, getterVisibility = Visibility.NONE, setterVisibility = Visibility.NONE)
abstract class SampleMixin {
#JsonCreator
public SampleMixin(#JsonProperty("id") int id) {
}
}
Deserilaization:
ObjectMapper mapper = new ObjectMapper();
mapper.addMixIn(Sample.class, SampleMixin.class);
Sample sample = mapper.readValue(json, Sample.class);
You can. Builder must meet certain requirements. For instance its methods for fields must have certain prefix, like "with" or "set".
Here is DTO class and its builder without any jackson annotations:
public class DtoBuilderWithFinalFieldsWithoutJackson {
public final String stringValue;
private DtoBuilderWithFinalFieldsWithoutJackson(final String stringValue){
this.stringValue = stringValue;
}
public static Builder builder(){
return new Builder();
}
public static class Builder {
private String stringValue;
public Builder withStringValue(String stringValue) {
this.stringValue = stringValue;
return this;
}
public DtoBuilderWithFinalFieldsWithoutJackson build() {
return new DtoBuilderWithFinalFieldsWithoutJackson(stringValue);
}
}
}
Without any additional effort with a default custom object you can serialize this dto object. You are responsible for instance creation. Jackson only needs to access fields. In our case this is a public field.
In case DTO is used for deserialization, you need to customize custom object:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setAnnotationIntrospector(new JacksonAnnotationIntrospector(){
#Override
public Class<?> findPOJOBuilder(AnnotatedClass ac) {
//set analogue of: #JsonDeserialize(builder = DtoBuilderWithFinalFieldsWithoutJackson.Builder.class)
if (DtoBuilderWithFinalFieldsWithoutJackson.class.equals(ac.getRawType())) {
return DtoBuilderWithFinalFieldsWithoutJackson.Builder.class;
} else {
return super.findPOJOBuilder(ac);
}
}
#Override
public JsonPOJOBuilder.Value findPOJOBuilderConfig(AnnotatedClass ac) {
if (DtoBuilderWithFinalFieldsWithoutJackson.class.equals(ac.getRawType())) {
//both build and with - are default in this case:
//set analogue of #JsonPOJOBuilder(buildMethodName = "build", withPrefix = "with")
return new JsonPOJOBuilder.Value("build", "with");
} else {
return super.findPOJOBuilderConfig(ac);
}
}
});
and use this customized CustomObject in your implementations. Here is a test and full example can be found here: DtoBuilderWithFinalFieldsWithoutJackson test

Avoid serialization of certain fields at runtime in Jackson

I have a controller which produces JSON, and from this controller, I return an entity object, which is automatically serialized by Jackson.
Now, I want to avoid returning some fields based on a parameter passed to the controller. I looked at examples where this is done using FilterProperties / Mixins etc. But all the examples I saw requires me to use ObjectMapper to serialize / de-serialize the bean manually. Is there any way to do this without manual serialization? The code I have is similar to this:
#RestController
#RequestMapping(value = "/myapi", produces = MediaType.APPLICATION_JSON_VALUE)
public class MyController {
#Autowired
private MyService myService;
#RequestMapping(value = "/test/{variable}",method=RequestMethod.GET)
public MyEntity getMyEntity(#PathVariable("variable") String variable){
return myservice.getEntity(variable);
}
}
#Service("myservice")
public class MyService {
#Autowired
private MyEntityRepository myEntityRepository;
public MyEntity getEntity(String variable){
return myEntityRepository.findOne(1L);
}
}
#Entity
#Table(name="my_table")
#JsonIgnoreProperties(ignoreUnknown = true)
public class MyEntity implements Serializable {
#Column(name="col_1")
#JsonProperty("col_1")
private String col1;
#Column(name="col_2")
#JsonProperty("col_2")
private String col2;
// getter and setters
}
Now, based on the value of "variable" passed to the controller, I want to show/hide col2 of MyEntity. And I do not want to serialize/deserialize the class manually. Is there any way to do this? Can I externally change the Mapper Jackson uses to serialize the class based on the value of "variable"?
Use JsonView in conjunction with MappingJacksonValue.
Consider following example:
class Person {
public static class Full {
}
public static class OnlyName {
}
#JsonView({OnlyName.class, Full.class})
private String name;
#JsonView(Full.class)
private int age;
// constructor, getters ...
}
and then in Spring MVC controller:
#RequestMapping("/")
MappingJacksonValue person(#RequestParam String view) {
MappingJacksonValue value = new MappingJacksonValue(new Person("John Doe", 44));
value.setSerializationView("onlyName".equals(view) ? Person.OnlyName.class : Person.Full.class);
return value;
}
Use this annotation and set the value to null, it will not be serialised:
#JsonInclude(Include.NON_NULL)

Jackson ignore all properties of superclass from external library

I am developing using an ORM where I extend a base orm class to create tables.
For example:
public class Person extends DbItem {
#JsonIgnore
private String index;
private String firstName;
private String lastName;
}
Problem is that when I use ObjectMapper to serialize, it tries to serialize the members of the DbItem class. Is there any simple way to prevent this? For example with an annotation.
I had a look at a similar problem Jackson serialization: how to ignore superclass properties but I was hoping it could be done simpler, and I'm not sure if I could do it as I can't change the superclass since it is in an external library.
You can use a Mix-in or #JsonIgnoreProperties
For the purposes of these examples, the base ORM class and extension are assumed to be:
public class DbItem {
public String dbPropertyA;
public String dbPropertyB;
}
and
public class Person extends DbItem {
public String index;
public String firstName;
public String lastName;
}
respectively.
Using a Mix-in
A Mix-in is an abstraction of the de/serialization instructions that Jackson understands from an object itself. It is a way to customize de/serialization of 3rd party classes. In order to define a Mix-in, an abstract class must be created and registered with the ObjectMapper.
Example Mix-in Definition
public abstract class PersonMixIn {
#JsonIgnore public String dbPropertyA;
#JsonIgnore public String dbPropertyB;
#JsonIgnore public String index;
}
Registering the Mix-in
#Test
public void serializePersonWithMixIn() throws JsonProcessingException {
// set up test data including parent properties
Person person = makeFakePerson();
// register the mix in
ObjectMapper om = new ObjectMapper()
.addMixIn(Person.class, PersonMixIn.class);
// translate object to JSON string using Jackson
String json = om.writeValueAsString(person);
assertFalse(json.contains("dbPropertyA"));
assertFalse(json.contains("dbPropertyB"));
assertFalse(json.contains("index"));
System.out.println(json);
}
#JsonIgnoreProperties
If you want to avoid creating a class and configuring the ObjectMapper, the #JsonIgnoreProperties annotation can be utilized. Simply annotate the class you are serializing and list the properties to exclude.
Example Serializable Object
#JsonIgnoreProperties({"index", "dbPropertyA", "dbPropertyB"})
public class Person extends DbItem {
public String index;
public String firstName;
public String lastName;
}
See It In Action
#Test
public void serializePersonWithIgnorePropertiesAnnotation() throws JsonProcessingException {
// set up test data including parent properties
Person person = makeFakePerson();
ObjectMapper om = new ObjectMapper();
// translate object to JSON string using Jackson
String json = om.writeValueAsString(person);
assertFalse(json.contains("dbPropertyA"));
assertFalse(json.contains("dbPropertyB"));
assertFalse(json.contains("index"));
System.out.println(json);
}
You want to do custom field level serialization. This will be a bit more work to maintain your code base, but is by far the simplest solution. See Jackson JSON custom serialization for certain fields for implementation details.

How to deserialize a generic object at runtime with Jackson

Say I have the following java classes (getters & setters omitted for brevity).
public class AllMyEvents {
private List<SomeEvent<?>> eventList;
}
public class SomeEvent<T> {
private long time;
#JsonProperty("event_type")
private String eventType;
#JsonProperty("event_data")
private T eventData;
}
public class BigEvent {
private List<SomeEvent<LittleEvent>> subEvents;
}
public class LittleEvent {
private long data;
}
When I call:
ObjectMapper om = new ObjectMapper();
AllMyEvents events = om.readValue(IOUtils.toString(jsonData, "UTF-8"),AllMyEvents.class);
The field eventData is of type LinkedHashMap. What I want is for this fields type to be specified by the eventType string. If the string is 'big' I want eventData to have type BigEvent or LittleEvent if the string is 'little'.
Is it possible to do this with Jackson annotations, or will I need to write a custom serializer/deserializer, or some other method? I'm using Jackson 1.9 if that is relevant.
Json Sub types is your answer.
#JsonTypeInfo(use=JsonTypeInfo.Id.NAME, include=JsonTypeInfo.As.PROPERTY, property="#class")
#JsonSubTypes({
#JsonSubTypes.Type(value=BigEvent.class, name="big"),
#JsonSubTypes.Type(value=LittleEvent.class, name="little")
})
public class SomeEvent<T> {
private long time;
#JsonProperty("event_type")
private String eventType;
...
Also see: http://wiki.fasterxml.com/JacksonPolymorphicDeserialization

Categories