Jackson reading all numbers as BigDecimal - java

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.

Related

Rest template ResponseEntity body is changing the types of variables, how to control the types?

I'm making a Post request from an app where i have the below
Set<Accounts> set = populateAccounts();
ResponseEntity<Map> responseMap =
restTemplate.postForEntity("http://localhost:8080/maptest", set, Map.class);
return responseMap.getBody();
And this request then returns a Map from responseMap.getBody();
And the below is the code where i receive the post request
#PostMapping("/maptest")
public ResponseEntity<Map> mapReturn(#RequestBody Set<Accounts> accounts) {
HashMap<String, Amount> map = new HashMap<String, String>();
map.put("account1", new Amount(BigDecimal.TEN));
map.put("account2", new Amount(BigDecimal.ZERO));
return ResponseEntity.ok(map);
}
Problem is, the Map that is returned does not have amounts as BigDecimal values, and these BigDecimal values are being automatically converted to an Integer when i see them in responseMap.getbody();
Please help me understand how i can maintain them as BigDecimal values.
Also, the actual code is slightly more elaborate than the above, but i wanted to keep it simple. I absolutely want to keep the values as BigDecimal, just not sure how.
You're probably using Jackson as the marchalling library for RestTemplate.
When you read the values the ObjectMapper reads numbers as ints/doubles etc.
You can easily set the behavior for deserialization using following configuration properties.
jackson-databind DeserializationFeature
USE_BIG_DECIMAL_FOR_FLOATS
Feature that determines whether JSON floating point numbers are to be deserialized into BigDecimals if only generic type description (either Object or Number, or within untyped Map or Collection context) is available.
USE_BIG_INTEGER_FOR_INTS
Feature that determines whether JSON integral (non-floating-point) numbers are to be deserialized into BigIntegers if only generic type description (either Object or Number, or within untyped Map or Collection context) is available.
To set it you can write as described here
ObjectReader r = objectMapper.reader(MyType.class);
// enable one feature, disable another
MyType value = r
.with(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)
.without(DeserializationFeature.WRAP_EXCEPTIONS)
.readValue(source);
or just
ObjectMapper mapper = new ObjectMapper();
mapper.enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS);
OptionTransaction transaction = mapper.readValue(jsonString, OptionTransaction.class);
You can of course write your own number deserializers and use #JsonDeserialize(using = CustomNumberDeserializer.class) annotation.
Similar technique is possible in other marchalling libraries (like Gson)

Jackson library 1.x array if one element sometimes just a string

I have to deal with strange Json messages.
there are Arrays in the schema, but if there are only one element array becomes an string.
So sometimes it is:
"Cisco-AVPair": [
"connect-progress=Call Up",
"nas-tx-speed=8083000",
"nas-rx-speed=8083000"
],
and sometimes:
"Cisco-AVPair": "connect-progress=Call Up".
How to overcome this if I use Jackson 1.8.2
I am afraid I am not in control of source code generation and only can parse it.
I do parse it with:
mapper.readValue(json, refType);
while my type reference is:
#JsonProperty("Cisco-AVPair")
private List<String> CiscoAVPair = new ArrayList<String>();
#JsonProperty("Cisco-AVPair")
public List<String> getCiscoAVPair() {
return CiscoAVPair;
}
#JsonProperty("Cisco-AVPair")
public void setCiscoAVPair(List<String> CiscoAVPair) {
this.CiscoAVPair = CiscoAVPair;
}
As you see it is list of strings, but sometimes comes just as a string.
There's a specific config option even in ancient Jackson 1.8.2 that accomplishes exactly what you need.
You should configure your ObjectMapper instance to always deserialize JSON values as a List, no matter whether values come as an array or as a single element. Please see javadocs here for the deserialization feature you need to enable, and these other javadocs to see how to actually activate/deactivate a feature on an ObjectMapper instance.
ObjectMapper mapper = ...;
mapper = mapper.configure(DeserializationConfig.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
Bear in mind that configure() method returns another instance of ObjectMapper.

Convert Json to a java object

What is the way to generate a Java object with get and set methods?
You should write a java bean with properties maching the JSON key's, from that point since you already have a reader its a simple as
YourObject obj = gson.fromJson(br, YourObject.class);
UPDATE
With respect to your comment, when you don't want or can't create a bean it usually boils down to parsing JSON to map. GSON (afaik) doesn't have a built-in for this, but its not hard to build a method that will traverse GSON's objects. You have an example in this blog
http://itsmyviewofthings.blogspot.it/2013/04/jsonconverter-code-that-converts-json.html
As you seem to be open to alternatives, take a look at Jackson as well (the two libs are the de-facto standard in JAVA).
With jackson you don't have to create a bean to support deserialization, e.g.
String json = "{\"id\":\"masterslave\"}";
Map<String,String> map = new HashMap<String,String>();
ObjectMapper mapper = new ObjectMapper();
//convert JSON string to Map
map = mapper.readValue(json,
new TypeReference<HashMap<String,String>>(){});
http://www.jsonschema2pojo.org/
That link helps generate the Java object format based on the GSON you feed in. Just make sure you set the settings exactly as you need it. As always, it's not a good idea to just copy-paste generated code, but it might be of help.

Android JSON Parsing (Jackson)

I've read a bunch of different articles, comparations and tutorials that are using different JSON-Libraries for parsing (and creating) JSON into Java Objects. Anyway I think that I've got the facts right cause I've decided to use the JSON library called Jackson.
GSON is simple and robust but way to slow acording to me. So I decided to actually try this Jackson thing out but, it seems like the parsing is a little bit more confusing here than with GSON.
The data-type of the value that I want to parse is simply an Boolean.
This is what the JSON that I'm trying to parse looks like:
{"FooResult":true}
So what I actually need help with is selecting the value from the key FooResult and then parse its value into an Boolean.
This Is what I've done so far:
String json = getString(request);
ObjectMapper mapper = new ObjectMapper();
mapper.readValue(json, Boolean.class);
But this code obviously gives me an error cause I haven't selected that it is the FooResult key that I'm interested in reading & parsing into an Boolean.
You should create a new class like this:
class MyClass {
public boolean FooResult;
}
And use this code to load the data:
MyClass myObject = mapper.readValue(json, MyClass.class);
Then you can access the value with myObject.FooResult
Ok this is lame. Even lamer when I rethink about it. The problem the whole time have been that the class of the object that you want to parse needs to be static. I've tried what Simon suggested like four or five times before I even posted this question today but the problem all time was that the class wasn't static.
So now it finally works.
static class FooClass
{
public boolean FooResult;
}
And for the parsing process.
String json = getString(request);
ObjectMapper mapper = new ObjectMapper();
FooClass fooClass = null;
try
{
fooClass = mapper.readValue(json, FooClass.class);
}
boolean result = fooClass.FooResult;

Specifying the field naming policy for Jackson

I have question related to bean to json serialziation/deserialization using Jackson. Previously I have used GSON to do that, but now I am faced with a project that already depends on Jackson and I would prefer not to introduce new dependency if I can do with what I already have at hand.
So imagine I have a bean like:
class ExampleBean {
private String firstField;
private String secondField;
// respective getters and setters
}
And then Jackson serializes it to:
{
"firstField": "<first_field_value>",
"secondField": "<second_field_value>"
}
I am using the following code to produce the above result:
ExampleBean bean;
...
ObjectMapper mapper = new ObjectMapper();
mapper.writeValue(outStream, bean);
However I would like (am expected) to get the following serialization:
{
"first_field": "<first_field_value>",
"second_field": "<second_field_value>"
}
I have deliberately simplified my example, but I have big hierarchy of beans that I want to serialize and I want to specify that the serialized attributes should always be in snake_style (that is with underscores) and the corresponding bean fields should always be camelCased. Is there any way I can enforce such field /attribute naming policies and use them without annotating the corresponding attribute for every field?
And yes I found it (it turned out that after 2 hours of searching I had been only 30 minutes away from finding it):
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(
PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
mapper.writeValue(outStream, bean);
Hopefully this will turn out to be helpful to somebody else too.
Now CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES is the deprecated strategy use SNAKE_CASE instead
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(
PropertyNamingStrategy.SNAKE_CASE);
mapper.writeValue(outStream, bean);

Categories