Jackson XML - #JsonSerialize serializer runtime configuration - java

All the examples of #JsonSerialize and JsonSerializer implementations are similar to the following.
public class JodaDateTimeJsonSerializer extends JsonSerializer<DateTime> {
private static final String dateFormat = ("MM/dd/yyyy");
#Override
public void serialize(DateTime date, JsonGenerator gen, SerializerProvider provider)
throws IOException, JsonProcessingException {
String formattedDate = DateTimeFormat.forPattern(dateFormat).print(date);
gen.writeString(formattedDate);
}
}
It has a class-level variable that defines its runtime behaviour. This cannot be changed at runtime via configuration.
How can I make dateFormat above a configurable property, while using #JsonSerialize(using = JodaDateTimeJsonSerializer.class) on a property?
Right now, I'm considering just making it a static non-final property on the serializer implementation that is set at runtime by the application on run. Doesn't make for a very proper implementation. Edit: The ideal solution will allow me to ~inject~ (edit: pass in) only the required configuration into the serializer, not retrieve configuration from some global.
Note: I do not want to create a new data type for this property (they are all Strings) and I do not want to create a custom bean serializer (they can be annotated on any String property anywhere). Because of this, I also cannot use SimpleModule::addSerializer(class, serializer) method as they should not be added to all String properties.

Hello some ResourceBundle implementations provide caching capabilities. This would say that looking up a property like date format in such situation will have no performance impact on your application and you can look it up straight in your method.
In order for any change in configuration to take effect at runtime. You should clear the ResourceBundle cache either at certain intervals or alternatively when you have update the configurations files. This can be easily achieved by invoking the method:
ResourceBundle.clearCache();

Related

com.fasterxml.jackson.databind.JsonSerializer exclude fields by key

I have a class which I do not have control over it's source:
public class SomeClassImpl implements SomeClass {
private SomeField someFiled; // Not serializable
... // Some other fields that are serializable
}
So this class is not fully Serializable, and I am running into StackOverflowError when I try to serialize it as json using Spring Boot as #ResponseBody.
I have two controller methods:
#ResponseBody public SomeClassImpl get();
#ResponseBody public SomeOtherClass find();
I have control over the source of SomeOtherClass which includes SomeClass as a property.
I could not figure out how to ignore a field using #JsonIgnore annotation, I probably need to to control the source.What I can do with annotations is that I can ignore SomeClass property from SomeOtherClass which does not help the first method above. So I decided to implement JsonSerializer<SomeClassImpl>:
#Override
public void serialize(SomeClassImpl someClass, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, JsonProcessingException {
// I need to write all fields except some
}
Or can I handle this with annotations? If yes how? Thanks.
When modifying the source code is not an option, you can use mix-in annotations to add Jackson annotations to a bean.
First define a mix-in annotation interface or class:
public interface FooMixIn {
#JsonIgnore
Object getBiz();
}
Then configure ObjectMapper to use the defined interface as a mix-in for your POJO:
ObjectMapper mapper = new ObjectMapper().addMixIn(Foo.class, FooMixIn.class);
All annotation sets that Jackson recognizes can be mixed in.
All kinds of annotations (member method, static method, field, constructor annotations) can be mixed in.
Only method (and field) name and signature are used for matching annotations: access definitions (private, protected, ...) and method implementations are ignored.
For more details, have a look at the Jackson documentation.

Jackson ObjectMapper set JsonFormat.Shape.ARRAY without annotation

I need to use two jackson 2 object mappers.
Both mappers work with the same set of classes.
In the first I need to use standard serialization.
In the second i want to use ARRAY shape type for all classes (see https://fasterxml.github.io/jackson-annotations/javadoc/2.2.0/com/fasterxml/jackson/annotation/JsonFormat.Shape.html#ARRAY).
But i want to global set this feature for my second ObjectMapper. Something like mapper.setShape(...)
How to do it?
UPD:
I found a way to override the config for the class:
mapper.configOverride(MyClass.class)
.setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.ARRAY));
So I can change for all my classes using Reflection API.
It is embarrassing that I override the global setting, but I can not directly set it.
As #JsonFormat annotation works on field, you can't set it to Shape.Array at global level. This would mean all the fields would be serialized and deserialised into array values (imagine if a field is already a list, in this case, it will be wrapped into another list which is something we might not want).
You can however, write your own serializer for a type (that converts a value into an array) and configure it in ObjectMapper, e.g.:
class CustomDeserializer extends JsonSerializer<String>{
#Override
public void serialize(String value, JsonGenerator gen, SerializerProvider serializers)
throws IOException, JsonProcessingException {
gen.writeStartArray();
gen.writeString(value);
gen.writeEndArray();
}
}
And configure it to ObjectMaper instance, e.g.:
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(String.class, new CustomDeserializer());
mapper.registerModule(module);

How prevent Jackson from serializing a field in a third party class

Heres a question for Jackson 2.3 I don't have the possibility to change to other frameworks.
I want to serialize objects to json using Jackson 2.3.
Some of the Objects are from a third party library implementing a particular (external) interface.
What I want to achieve is to prevent certain fields in those objects to be serialized.
I do not have access to modifying this class so #JsonIgnore wont cut it.
Heres an example
public interface ThirdParty{
public char[] getPassword();
public String getUser();
public Department getDepartment();
}
I'm new to Jackson, but have a feeling that it should be fairly simple to do something like this
ObjectMapper mapper=new ObjectMapper();
SimpleModule testModule = new SimpleModule("DemoModule",VersionUtil.versionFor(this.getClass()));
testModule.addSerializer(ThirdParty.class, new Some_Serializer_That_Can_Ignore_Password()));
mapper.registerModule(testModule);
(I don't think there is something called a Some_Serializer_That_Can_Ignore_Password, what I want is something that does NOT serialize the field)
I would prefer not to write a lot of code to make it work, I've seen quite verbose examples for older versions of Jackson, but none for Jackson 2.3.
Thanks!
Not really an answer for the original question, but I found a way that worked for excluding particular types, this code ensures that any StackTraceElements are not serialized.
SimpleModule testModule = new SimpleModule("DemoModule", VersionUtil.versionFor(this.getClass()));
testModule.addSerializer(StackTraceElement.class,new JsonSerializer<StackTraceElement>() {
#Override
public void serialize(StackTraceElement value, JsonGenerator jgen, SerializerProvider provider){/*Do not serialize*/}
});
mapper.registerModule(testModule);

how to use method canDeserialize ObjectMapper class from jackson API?

I want to use method canDeserialize, because at moment deserialization i want to get type class for apply at custom deserialization, as about next example :
public T deserialize(byte[] bytes) throws SerializationException {
bolean isAccount = this.objectMapper.canDeserialize(??????).
T t = null;
if(isAccount)
t = (T)this.objectMapper.readValue(bytes,Account.class);
else
t = (T) this.objectMapper.readValue(bytes, 0, bytes.length, new TypeReference<Object>(){});
return t;
}
In this case Account class have annotation #JsonDeserialize for a custom deserialization .
To directly answer your question, this is how you use the canDeserialize method:
final ObjectMapper mapper = new ObjectMapper();
mapper.canDeserialize(mapper.constructType(Bean.class));
Where Bean is the name of your Java class to be checked.
But wait, you are trying to solve the wrong problem. You are struggling with the logic for your method because it has not been designed properly. You are really asking too much of the Java runtime (and Jackson library), by trying to make them infer all the required information about the type to be instantiated (based on the parameterized return). To solve this you should include the class representing the type to be deserialized as a parameter to the method, greatly simplifying the logic:
public <T> T deserialize(byte[] bytes, Class<T> clazz) throws IOException,
JsonProcessingException {
return new ObjectMapper().readValue(bytes, clazz);
}
At this point you have probably realized that the method above provides no additional functionality over just calling ObjectMapper.readValue directly, so ... just do that! No need to define custom methods, just use ObjectMapper and you are good to go. Keep in mind that you do not need to do anything explicit to trigger custom deserialization of classes. The Jackson runtime automatically detects when a class has a custom deserializer and invokes it.

Jackson JSON library: how to instantiate a class that contains abstract fields

I want to convert a JSON string into java object, but the class of this object contains abstract fields, which Jackson can't instantiate, and doesn't produce the object. What is the easiest way to tell it about some default implementation of an abstract class, like
setDefault(AbstractAnimal.class, Cat.class);
or to decide about the implementation class based on JSON attribute name, eg. for JSON object:
{
...
cat: {...}
...
}
i would just wite:
setImpl("cat", Cat.class);
I know it's possible in Jackson to embed class information inside JSON, but I don't want to complicate the JSON format I use. I want to decide what class to use just by setting default implementation class, or by the attribute name ('cat') - like in XStream library, where you write:
xStream.alias("cat", Cat.class);
Is there a way to do so, especially in one line, or does it require some more code?
There are multiple ways; before version 1.8, simplest way is probably to do:
#JsonDeserialize(as=Cat.class)
public abstract class AbstractAnimal { ... }
as to deciding based on attribute, that is best done using #JsonTypeInfo, which does automatic embeddeding (when writing) and use of type information.
There are multiple kinds of type info (class name, logical type name), as well as inclusion mechanisms (as-included-property, as-wrapper-array, as-wrapper-object). This page: https://github.com/FasterXML/jackson-docs/wiki/JacksonPolymorphicDeserialization explains some of the concepts.
A full fledged answer with a very clear example can be found here: https://stackoverflow.com/a/30386694/584947
Jackson refers to this as Polymorphic Deserialization.
It definitely helped me with my issue. I had an abstract class that I was saving in a database and needed to unmarshal it to a concrete instance of a class (understandably).
It will show you how to properly annotate the parent abstract class and how to teach jackson how to pick among the available sub-class candidates at run-time when unmarshaling.
If you want to pollute neither your JSON with extra fields nor your classes with annotation, you can write a very simple module and deserializer that uses the default subclass you want. It is more than one line due to some boilerplate code, but it is still relatively simple.
class AnimalDeserializer extends StdDeserializer<Animal> {
public AnimalDeserializer() {
super(Animal.class);
}
public Animal deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException {
return jsonParser.readValueAs(Cat.class);
}
}
class AnimalModule extends SimpleModule {
{
addDeserializer(Animal.class, new AnimalDeserializer());
}
}
Then register this module for the ObjectMapper and that's it (Zoo is the container class that has an Animal field).
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.registerModule(new AnimalModule());
return objectMapper.readValue(json, Zoo.class);
The problem can be solved with the annotation #JsonDeserialize on the abstract class.
Refers to Jackson Exceptions Problems and Solutions for more info

Categories

Resources