I'm looking for a way to configure jackson deserializer to ignore some fields. I don't want to achieve this by annotating model since It's out given by another project; I just want to do it by constructing deserializer (ObjectMapper) to do so.
Is it possible?
You can achieve that using Mix-In annotation.
class ThirdPartyReadOnlyClass {
private String ignoredPropertyFromThirdParty;
public String getIgnoredPropertyFromThirdParty() {
return ignoredPropertyFromThirdParty;
}
}
abstract class MixIn {
#JsonIgnore
String getIgnoredPropertyFromThirdParty();
}
You can put json annotations on MixIn class as if you are putting them on original model class.
Configuring object mapper
objectMapper.addMixInAnnotations(ThirdPartyReadOnlyClass.class, MixIn.class);
you have to do following
1)write your own Deserializer which extends JsonDeserializer
2) override deserialize method and return your class object after ignoring some of the fields
3) register your deserializer with ObjectMapper
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(yourClass.class, new yourDerializer());
mapper.registerModule(module);
String newJsonString = "{\"id\":1}";
final yourClass yourClassObject= mapper.readValue(newJsonString, yourClass.class);
Hope this will solve your problem
Related
I was trying to implement a custom serializer for one of the properties of my object to get a different JSON structure when I return it from my REST controller.
My constraints are I cannot change the interface of the REST controller or the model classes (so I cannot add extra annotation etc, that would maybe make this easier). The only thing I could think of, making it render different than described in the model is a custom serializer, if there are any better approaches for this, please don't hesitate to tell me a different approach that is within the constraints.
My models look something like this:
public class WrapperModel {
// a lot of autogenerated fields
List<Property> properties;
// getters/setters
}
public class Property {
private String name;
private String value;
// getters / setters
}
So when this is rendered is looks like so:
{ ....
"properties": [
{"key1": "value1"}, {"key2": "value2"},...
]
}
What I would want is this:
{ ....
"properties": {
"key1": "value1",
"key2": "value2",
...
}
}
The serializer for this is easy enough:
public class PropertyListJSONSerializer extends StdSerializer<List<Property>> {
//....
#Override
public void serialize(List<Property> value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeStartObject();
for(Property p: value){
gen.writeStringField(p.getName(), p.getValue());
}
gen.writeEndObject();
}
}
Now when I try to register this serializer inside a #Configuration file:
#Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);
SimpleModule module = new SimpleModule();
module.addSerializer(List<Property>.class, new PropertyListJSONSerializer());
mapper.registerModule(module);
return mapper;
}
this doesn't work, because List<Property>.class cannot be used for addSerializer since it's a template class. Is there any other way to add this serializer or something that does something similar?
I do not want to add a custom serializer for WrapperModel since this class is autogenerated and fields can be added and removed. This should be possible without modifying the application code (if I had a custom serializer you would need to add/remove the fields from the serializer also(?)). Or is there a way to just use the Standard serializer for the class and just manually handle this one List<> field.
The model classes are generated by the Spring Boot openapi code generator, so there is a very limited set of JSON annotations I can put on top of the model fields (if there's an annotation way, please dont hesitate to post as I can check in the openapi sourcecode if that particular annotation is supported). But I would rather go with either a custom serializer for List<Property> if that is at all possible or writing a serializer for WrapperModel that uses StdSerializer for everything and only handle the List property myself.
MixIn
In that case we need to use MixIn feature. Create interface like below:
interface WrapperModelMixIn {
#JsonSerialize(using = PropertyListJSONSerializer.class)
List<Property> getProperties();
}
and register it like below:
ObjectMapper mapper = new ObjectMapper();
mapper.addMixInAnnotations(WrapperModel.class, WrapperModelMixIn.class);
Older proposal
You need to use Jackson types which allow to register serialiser for generic type. Your serialiser after change could look like below:
class PropertyListJSONSerializer extends StdSerializer<List<Property>> {
public PropertyListJSONSerializer(JavaType type) {
super(type);
}
#Override
public void serialize(List<Property> value, JsonGenerator gen, SerializerProvider provider)
throws IOException {
gen.writeStartObject();
for (Property p : value) {
gen.writeStringField(p.getName(), p.getValue());
}
gen.writeEndObject();
}
}
And you can register it as below:
ObjectMapper mapper = new ObjectMapper();
CollectionType propertiesListType = mapper.getTypeFactory().constructCollectionType(List.class, Property.class);
SimpleModule module = new SimpleModule();
module.addSerializer(new PropertyListJSONSerializer(propertiesListType));
mapper.registerModule(module);
I have a problem with deserialization json into POJO class which looks like this:
#Data
public class Foo {
private String fieldA;
private String fieldB;
private IBar fieldC;
}
IBar is an interface which defines getters for some classes. One of the solutions what I found is to use #JsonDeserialize(as = BarImpl.class) where BarImpl will implement IBar interface. Problem is classes which implement that interface (for instance BarImpl) are in another maven module where I don't have access from current module so I cannot use one of this impl classes in that annotation. Can you tell me if there is another solution?
Thank you in advice.
Are you sure you mean deserialization? You'll need a concrete implementation of your interface if Jackson's going to be able to create Java objects for you.
deserialization = Json String -> Java object
serialization = Java object -> Json String
When serializing Jackson will use the runtime class of the object, so it will use the actual implementations rather than attempt to use the interface. If you want to customize this you can add a serializer for your interface. You'll need to decide exactly what you want to write out.
ObjectMapper objectMapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(IBar.class, new JsonSerializer<IBar>() {
#Override
public void serialize(IBar value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
gen.writeStartObject();
gen.writeStringField("fieldName", value.getFieldName());
gen.writeEndObject();
}
});
objectMapper.registerModule(module);
I have class with #JsonIgnore-d field:
public class MyClass {
...
#JsonIgnore
private SomeType myfield;
...
// getters & setters
}
Is it possible to configure ObjectWriter so that it includes myfield during serialization even though being ingored?
Rationale: MyClass is serialized in many places and only in single specific one I want to have myfield.
It is possible to configure ObjectMapper to disable a JsonIgnore function. Following are some possible solution you can try with:
1.
Disable JsonIgnore function for a particular annotated field.
You can create a custom JsonIgnore annotation and a custom JacksonAnnotationIntrospector to remove the annotation from mapper context.
Following are the ideas:
Annotate #MyJsonIgnore to the fields that should be ignored while serialization:
#JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
public class MyClass {
#MyJsonIgnore
private SomeType myField;
}
#MyJsonIgnore is a simple custom annotation that wrap #JsonIgnore:
#Retention(RetentionPolicy.RUNTIME)
#JacksonAnnotationsInside
#JsonIgnore
public #interface MyJsonIgnore {
}
A custom JacksonAnnotationIntrospector is implemented to remove #MyJsonIgnore from mapper context:
public class DisablingMyJsonIgnoreIntrospector extends JacksonAnnotationIntrospector {
#Override
public boolean isAnnotationBundle(final Annotation ann) {
if (ann.annotationType().equals(MyJsonIgnore.class)) {
return false;
} else {
return super.isAnnotationBundle(ann);
}
}
After that, you can set the introspector on a ObjectMapper during configuration:
ObjectMapper mapper = new ObjectMapper();
mapper.setAnnotationIntrospector(new DisablingMyJsonIgnoreIntrospector());
It results that the fields annotated with #MyJsonIgnore can be marshaled properly.
2.
Disable JsonIgnore function for the mapper
Your can create a custom JacksonAnnotationIntrospector and override hasIgnoreMarker method to always return false:
public static class DisablingJsonIgnoreIntrospector extends JacksonAnnotationIntrospector {
#Override
public boolean hasIgnoreMarker(final AnnotatedMember m) {
return false;
}
}
hasIgnoreMarker is to check whether there is annotation to ignore json property. Return false will disable the JsonIngore function.
3.
Disable all annotations and specify what kinds of properties are auto-detected for a given ObjectMapper:
final ObjectMapper mapper = new ObjectMapper();
mapper.disable(MapperFeature.USE_ANNOTATIONS);
mapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);
This simply disable all annotations.
Hope this can help.
One more option is to use the AnnotationIntrospector.nopInstance() if you want to avoid all Jackson's annotations in your pojo including #JsonIgnore e.g.
JsonMapper.builder().annotationIntrospector(AnnotationIntrospector.nopInstance()).build()...
or
new ObjectMapper().setAnnotationIntrospector(AnnotationIntrospector.nopInstance())...
I have a map that unsure it keys,but I am sure the keys contains all the pojo fields, says:
public class MyPojo{
String name,
String addr
}
//map contains keys that not in MyPojo field,e.g. age
map = {"name":"john","addr":"sf school","age":"21"}
In Java how can I can convert it to pojo MyPojo instance? the following method throw exception:
final ObjectMapper mapper = new ObjectMapper(); // jackson's objectmapper
final MyPojo pojo = mapper.convertValue(map, MyPojo.class);
You can use #JsonIgnoreProrperties by Jackson in your MyPojo class.
The exception is cause of ObjectMapper not being able to find exact mapping in your MyPojo class.
The API is provided in the same library, that of ObjectMapper.
So here is what your class should look like:
#JsonIgnoreProperties(ignoreUnknown = true)
public class MyPojo{
String name;
String addr;
//Other variables
}
To import it in your code, you need to add the following:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
I hope this solves your problem and thisis exactly what you're looking for.
As you are working on generics keys(previously unknown), there might be chances of keys(attribute names) present in the map as a key doesn't present in POJO as an attribute. So, set below properties to false, when you create an ObjectMapper instance, so that any unknown or missing attributes wouldn't throw any exceptions.
final ObjectMapper mapper = new ObjectMapper(); // jackson's objectmapper
mapper.configure(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES, false);
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
I have the following scenario:
public class A {
#JsonProperty("member")
private Integer Member;
}
public class B {
private Integer Member;
}
Now, I wish to do the following:
ObjectMapper mapper = new ObjectMapper();
B b = new B(); b.setMember(1);
A a = mapper.converValue(b, A.class);
Ordinarily, this would work. However, since the objectMapper takes annotations such as #JsonProperty into account, I get the following result:
A.getMember(); // Member = NULL
There is a workaround, where all fields that are expected to be null due to this are set manually, i.e. A.setMember(b.getMember());, but this defeats the purpose of using the objectMapper in the first place and is potentially error-prone.
Is there a way to configure the objectMapper to ignore the #JsonProperty fields of a given class (or globally)?
You can configure the ObjectMapper to ignore annotations like #JsonProperty by doing:
ObjectMapper objectMapper = new ObjectMapper().configure(
org.codehaus.jackson.map.DeserializationConfig.Feature.USE_ANNOTATIONS, false)
.configure(org.codehaus.jackson.map.SerializationConfig.Feature.USE_ANNOTATIONS, false)
But this will cause it to also ignore things like #JsonIgnore etc. I'm not aware of any way to make the ObjectMapper ignore only specific annotations.
To ignore all annotations the syntax in Jackson version 2.x is:
objectMapper.configure(MapperFeature.USE_ANNOTATIONS, false)
Just ignoring a subset seems to be not possible with this approach.
But a much better solution can be found in this answer: https://stackoverflow.com/a/55064740/3351474
For your needs it should be then:
public static class IgnoreJacksonPropertyName extends JacksonAnnotationIntrospector {
#Override
protected <A extends Annotation> A _findAnnotation(Annotated annotated, Class<A> annoClass) {
if (annoClass == JsonProperty.class) {
return null;
}
return super._findAnnotation(annotated, annoClass);
}
}
...
mapper.setAnnotationIntrospector(new IgnoreJacksonPropertyName());