I'm trying to use Jackson in case to serialize and then deserialize an object. The object contains a field -> protected Serializable data
This is where the problem comes from - this data can come from a lot of components. That is why I'm trying to configure the Jackson ObjectMapper instead of adding annotations and changing the code (because for some of the issues I have with the serialization, like this one, I have to change like hundreds of different classes). Is there any way to tell the ObjectMapper to not use .booleanValue() on null fields.
This is how I've currently configured my ObjectMapper:
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(objectMapper.getSerializationConfig().getDefaultVisibilityChecker()
.withFieldVisibility(JsonAutoDetect.Visibility.ANY)
.withGetterVisibility(JsonAutoDetect.Visibility.NONE)
.withSetterVisibility(JsonAutoDetect.Visibility.NONE)
.withCreatorVisibility(JsonAutoDetect.Visibility.NONE));
This usually happens when your object has a property definition of type Boolean but a getter of type boolean. Ensure these types match.
I'm looking at a REST API which uses DropWizard. DropWizard appears to somehow magically know how to serialize joda DateTime objects to a string formatted like this:
YYYY-MM-DD HH:MM:SS
But if I write a test which uses an ObjectMapper to serialize a joda DateTime object, I get something like:
{"month_of_year":1,"hour_of_day":0,"minute_of_hour":15,"second_of_minute":3,"millis_of_second":0,"week_of_weekyear":3,"weekyear":2018,"year_of_era":2018,"year_of_century":18,"century_of_era":20,"millis_of_day":903000,"second_of_day":903,"minute_of_day":15,"day_of_year":18,"day_of_week":4,"day_of_month":18,"year":2018,"era":1,"millis":1516259703000,"chronology":{"zone":{"uncached_zone":{"cachable":true,"fixed":false,"id":"America/Denver"},"fixed":false,"id":"America/Denver"}},"zone":{"uncached_zone":{"cachable":true,"fixed":false,"id":"America/Denver"},"fixed":false,"id":"America/Denver"},"after_now":true,"before_now":false,"equal_now":false}
How does DropWizard know how to serialize this?
The field I'm serialized is annotated like this:
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "YYYY-MM-dd HH:mm:ss")
DateTime myDate;
I understand that I'm supplying a format string there, but I'd like to understand why ObjectMapper (in my test) doesn't respect it, while DropWizard does. (The annotation is a Jackson annotation, not a DropWizard one, so it confuses me that ObjectMapper.writeValue() wouldn't respect it).
When looking at the source of the #JsonFormat annotation, I see:
/**
* Datatype-specific additional piece of configuration that may be used
* to further refine formatting aspects. This may, for example, determine
* low-level format String used for {#link java.util.Date} serialization;
* however, exact use is determined by specific <code>JsonSerializer</code>
*/
public String pattern() default "";
This leads me to believe that Jackson doesn't actually supply the JsonSerializer that recognizes this pattern attribute, and at the same time leads me to believe that DropWizard does have such a JsonSerializer. How can I configure/modify/override the JsonSerializer that DropWizard is apparently using?
Your test is not using the right ObjectMapper. DW in its bootstrap will configure the ObjectMapper for you with reasonable defaults (that's DW's whole thing - everything is somewhat reasonable and pre-configured).
So, in your case, your test would need to use Jackson.newObjectMapper(). Try this:
public static void main(String[] args) throws JsonProcessingException {
ObjectMapper myMapper= new ObjectMapper();
ObjectMapper dwMapper = Jackson.newObjectMapper();
System.out.println(myMapper.writeValueAsString(new X()));
System.out.println(dwMapper.writeValueAsString(new X()));
}
public static class X {
#JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "YYYY-MM-dd HH:mm:ss")
DateTime now = DateTime.now();
}
Will print:
{"now":{"era":1,"dayOfYear":18,"dayOfWeek":4,"dayOfMonth":18,"year":2018,"weekOfWeekyear":3,"millisOfDay":37615193,"monthOfYear":1,"hourOfDay":10,"minuteOfHour":26,"secondOfMinute":55,"millisOfSecond":193,"weekyear":2018,"yearOfEra":2018,"yearOfCentury":18,"centuryOfEra":20,"secondOfDay":37615,"minuteOfDay":626,"zone":{"fixed":false,"uncachedZone":{"cachable":true,"fixed":false,"id":"Europe/London"},"id":"Europe/London"},"millis":1516271215193,"chronology":{"zone":{"fixed":false,"uncachedZone":{"cachable":true,"fixed":false,"id":"Europe/London"},"id":"Europe/London"}},"afterNow":false,"beforeNow":true,"equalNow":false}}
{"now":"2018-01-18 10:26:55"}
And for completeness, these are the modules that are registered for you:
private static ObjectMapper configure(ObjectMapper mapper) {
mapper.registerModule(new GuavaModule());
mapper.registerModule(new LogbackModule());
mapper.registerModule(new GuavaExtrasModule());
mapper.registerModule(new JodaModule());
mapper.registerModule(new AfterburnerModule());
mapper.registerModule(new FuzzyEnumModule());
mapper.registerModules(new Jdk8Module());
mapper.registerModules(new JavaTimeModule());
mapper.setPropertyNamingStrategy(new AnnotationSensitivePropertyNamingStrategy());
mapper.setSubtypeResolver(new DiscoverableSubtypeResolver());
return mapper;
}
These are all overwritable in the bootstrap.
Please not that I might be on an older version of DW, so it might be slightly different in the latest.
Artur
We are using Jackson to read json from the filesystem and parse it to the POJO.
POJO
String name;
Map<String,Object> map;
getters/setters
Reading
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
Pojo obj = mapper.readValue(jsonFile, Pojo.class);
Problem
When we have numbers in json (map part) they gets converted to Integer Or Double.And we want all our numbers (decimal and whole) as Type BigDecimal So I tried using the
mapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
But this only works for the decimal numbers. There is no feature available to covert whole numbers to BigDecimal.
Question
Do we have any inbuilt feature to enable ObjectMapper to read all the number to BigDecimal?
If I need to write custom serializer, Do I need to write it for the whole class or it can be written for Map field ?
You can register a Module with your ObjectMapper that includes a custom JsonDeserializer. You don't need to make your own because Jackson Databind provides a BigDecimalDeserializer but you need to do one more thing to make it work.
Because BigDecimalDeserializer is annotated with #JacksonStdImpl, Jackson won't allow you to use this directly because the initialization code (I currently disagree with this) specifically checks for this annotation and disallows it. Because this deserializer is not a final, you can get around this without copy-pasting by creating an anonymous subclass.
In the end, it will look something like this
Module module = new SimpleModule();
module.addDeserializer(Number.class, new NumberDeserializers.BigDecimalDeserializer() {});
new ObjectMapper().registerModule(module).readValue("{}", Map.class);
Try putting :BigDecimal:[dps] is in your json. For example :
{
"MY_BIG_D:BigDecimal:0" : 3
}
where dps = decimal places.
In Play Framework we have the helper method Json.toJson() to generate JsonNodes from the request body or DB queries, but I don't know how to remove specific fields from the object after it has been generated.
Play uses FasterXML/jackson under the hood.
For example, let's say that you want to retrieve the payload from a request. You call request().body().asJson(), in your controller and you get a JsonNode.
A JsonNode doesn't have insertion capabilities but ObjectNode has.
1. Creating an ObjectNode(showing 2 common ways to do it):
a. Casting to ObjectNode
ObjectNode json = (ObjectNode) request().body().asJson();
b. Using an ObjectMapper(gives you more control like serialization features)
ObjectMapper mapper = new ObjectMapper();
//set serialization features in cases where you need them
mapper.configure(SerializationFeature.WRITE_EMPTY_JSON_ARRAYS, false);
mapper.setSerializationInclusion(Include.NON_NULL);
ObjectNode json = mapper.createObjectNode();
2. Adding/removing elements (linked the ObjectNode API so you can check all of the available methods)
json.remove("fieldName");
json.put("anotherFieldName", "yesWeCan")
.put("canWeDoBoolean", true)
.put("howAboutNumbers", 1234567890);
Don't forget to check the rest of the documentation/tutorials as jackson is a complex library and you might want to educate yourself on the subject.
I have a map Map<String, Object> and some values are of type java.sql.Timestamp. I want to create a JSON node object using Jackson that would convert java.sql.Timestamp to StringNode node using method valueToTree. Using default ObjectMapper, java.sql.Timestamp is converted LongNode.
Extend JsonSerializer class, for a specific type and include that in the mapper via Module
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addSerializer(TimeStamp.class, new TimeStampSerializer());
mapper.registerModule(module);
APIs might differ based on the version being used.