Jackson ObjectMapper set JsonFormat.Shape.ARRAY without annotation - java

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

Related

Jackson XML - #JsonSerialize serializer runtime configuration

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

Jackson ObjectMapper - does changing configurations on ObjectReader impact usage of ObjectMapper by multiple threads

We have a Spring based application and we have defined a singleton bean for Jackson ObjectMapper class.
#Bean(name = "jacksonObjectMapper")
public ObjectMapper createObjectMapper() {
return new ObjectMapper();
}
We have a use case to write a generic JSON Serializer/Deserializer which we wrote in following way:
public <T, U> T deserialize(final String inputJsonString, final Class<T> targetType, final Class<U> targetParameterType) throws JsonProcessingException, IOException {
return objectMapper
.reader(objectMapper.getTypeFactory().constructParametricType(targetType, targetParameterType))
.without(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)
.readValue(inputJsonString);
}
Here I am using ObjectReader instead of ObjectMapper itself and changing some configurations on ObjectReader (e.g. .without(...)). My question is, will such configuration changes impact other threads which may be using the same ObjectMapper instance to do something else (may be simply deserializing or serializing)?
Could someone help me understand the details and guide me?
My apologies if I haven't explained the question clearly; please let me know and I can provide further details.
Short answer: No, ObjectReader configuration does not change the underlying ObjectMapper configuration.
Explanation: If you use the ObjectMapper directly and alter the configuration of the mapper it can lead to problems if the mapper is shared between multiple threads. However, if you do not change the underlying config you should be safe anyway (more reading here).
In your case you are calling the mapper.reader(...) method where you actually create an ObjectReader. The ObjectReader and ObjectWriter are immutable and therefore they never change the underlying state. Furthermore, even if you change the config of the underlying ObjectMapper the reader will not be affected.
Note that for each call to the mapper.reader(...) method you are creating a new ObjectReader so if you change your ObjectMapper config between calls to the reader method you may run into problems.
So, to summarize: If you create an ObjectReader and use the same reader in your thread you are safe.

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 2.0 Serialization config in writeValue method

Currently we are using Jackson 1.9.x in our application and have following code:
Object objectMapper = new ObjectMapper();
.....
SerializationConfig config = getConfig();
objectMapper.writeValue(jg, value, config);
As I understand during investigation (see SerializationConfig) in Jackson 2.0 (de)serialziation was changed and cannot be attached directly to objectMapper and my question is: What is correct replacment for the last sentence in code snippet?
Thanks in advance.
Usually you would create an ObjectWriter, and reconfigure that if necessary:
ObjectWriter w = mapper.writer(....); // various configuration methods
w.writeValue(jg, value);
Underlying configuration objects are hidden on purpose, as ObjectReader and ObjectWriter are immutable and thread-safe, so you can share and pass those instead of config objects.

Categories

Resources