-
-
Notifications
You must be signed in to change notification settings - Fork 1.4k
Deserializing BigDecimal using JsonNode loses precision #2087
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
Quick question: could this be same as #1770 ? |
I meet this problem too; JsonNode node = jp.getCodec().readTree(jp); |
Yes indeed, I just noticed it because my accounting reconciliation processing was giving me unexpected results. This bug should at least be prioritize as high since it's impact is extremely disastrous for anything where number accuracy matter (ie: financial transactions !) |
@ymenager Please do tell me how I use my free time. I really like hearing FUCKING DEMANDS FROM YOU, A PERSON I DON'T EVEN KNOW. Actually, fuck you. Go away. |
jeez no need to go on name calling, and I didn't say it had to be FIXED urgently, i said it had to be prioritized urgently.... not the same thing in the open source world (people just work on whatever they want, but they need to know which issues need to be addressed urgently or not) right now anyway who looks at the issue's labels can't see this is actually an extremely severe issue (data corruption is the most disastrous issue a data read/write framework can have) |
@cowtowncoder Do you need a hug? |
Honestly it just sounds like you're using Oracle DB and float data column types - you get the same thing. So it works like this @ymenager Double doesn't really have a "precision", or one that you can set anyway, so it adopts the ISO standard, when porting a double into a BigDecimal (which works on scale/precision) you need to be aware of a few things
So, down to the grit. e.g.
If you run this you can see what is wrong, you cannot go directly from double to BigDecimal without specifying a rounding. They are different storage and formula based mechanisms. Hope this helps, I have another one which goes into more detail (especially with handling BigDecimal.Zero) |
@ymenager Here #1568 (comment) It seems this misunderstanding of doubles and big decimals is quite common |
@GedMarc Yes I know very well what you mean in regards to double / float and precisions. The problem is those numbers should NEVER be stored as double or floats in the first place, but always be stored in a reliable format ( BigDecimal ). There are use cases where losing precision is acceptable, a json parsing library is definitely NOT one of them. And again, although it might have interpreted that way ( I might have not worded things the best way and for that I do apologise ), I was not asking to prioritise someone's time... I was talking about prioritizing the issue (ie... the severity label) I know this is an open source project (I've been doing OS, so I don't expect ANYONE to actually go and fix it, however I do expect issues to properly triaged so that anyone who looks at the issues can see what issues might affect them, and then if they are interested they can go and fix it. This is ESPECIALLY important for silent and random data corruption issue which might affect many people without them being aware of it, without potentially very bad or even catastrophic results especially for such a widely used library !!!! ( How many people use jackson for finance or health related projects ??????? ) |
@cowtowncoder Can close. |
@ymenager parsing of |
@GedMarc Although I might be highlighting the worse case scenarios, they are quite real and not "end of the world scenarios". I've worked most of my life on mission critical system for Banks, Telecoms, Governments so I have a deep appreciation of the real world consequences of the difference between a number going in as "3.70" and coming out as "3.7" (to quote exactly what i saw happen). Even on a smaller scale those bugs can have severe consequences. The effect of that bug could have gotten me personally in trouble with the tax office and gotten me accused of tax fraud (!) not a minor issue (luckily I'm rather paranoid and test and double-check everything manually) Also, from what I had read in this issue and another referred, doing those two features does NOT solve the problem (unless the information I read was incorrect or i mis-read it ? I didn't actually go verify it.... I'll give it a try) @plokhotnyuk Yes you have a very good point about potential abuse of the data being stored in BigDecimal format. Makes me think the safest way to handle this would have been for jackson to always store those as strings no matter what, and then only use "on demand" conversion... But that's probably too big a change to implement and not realistic |
Enough - the problem between the keyboard and the chair. As you said, there are literally thousands of finance (including mine) and transaction heavy businesses running the software, You're the only to have this problem. Find the denominator. It's a simple configuration of the ObjectMapper, which obviously @cowtowncoder knew, but your attitude is a really big problem. Your test case works when in the correct config, your problem is solved, just a lack of understanding on the API, and angry emotion driven responses to everyone trying to help you. |
Oh actually after running that specific code in this ticket, i see the issue is slightly different from what I was looking at (which was that after parsing the value using jsontree it was storing internally the value as Double although that flag was enabled to use BigDec, basically before involving any kind of data binding)...... So I might have over-reacted or at the very least ranted in the wrong ticket :) I guess I'm so used to see major consequences of small bugs of that category that it's made me oversensitive on the topic, and it rubbed me wrong when i thought it was a "known issue" that seemed ignored. I'll go relook at the issue I saw to double check |
@ymenager Thank you, Much appreciated With that description, it changes a bit.
You aren't assigning the configured instance back into "m", so technically it was never actually "configured" or "used". with/without/configure, use the returned instance.
|
@GedMarc I did had USE_BIG_DECIMAL_FOR_FLOATS, and then when i debugged the code it was popping up as Double in the nodetree (so instant potential data corruption). Which led to me think there was a bug where when using nodetree it ignored USE_BIG_DECIMAL_FOR_FLOATS (and the content of this issue and another one I saw made me think that was a known issue) However I was using Spring Boot, and the ObjectMapper was in a constructor class that was on a package that wasn't being picked up by the annotation scanner, so spring boot "helpfully" automatically created a default one (because it uses it for the REST), so it wasn't visible that the object mapper was not really the one I had configured :( After checking by hardcoding the Object Mapper (rather than using injected one), I can see that it has now created a DecimalNode which is what i expected rather than using a Double. So I do apologise for my overreaction, too much dealing with nasty data corruption bugs from "corporate developers" (the majority of which don't even seem to know that BigDec exists sigh) might have made me a bit too oversensitive on that topic |
Ok. So, I am back & apologies for over-reacting as well. I did read more into comment than was probably intended, but I can see how it was not meant the way I read it. I have nothing against anyone explaining why issue is critical and making their case. I see there has been in-depth discussion on actual issues on handling of fractional numbers, which is good. Now, looking back at the original description, I can see couple of potential problems due to shuffling back and forth via streaming (getCodec().parseTree(); m.treeToValue()), and possibly missing configuration.
although it not being enabled probably makes this non issue. But. Fundamentally I am not sure it is possible to fully retain One suggestion I have, however, is to streamline custom deserializer like so: @Override
public TestClass deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
// JsonNode node = jp.getCodec().readTree(jp);
JsonNode node = ctxt.readTree(jp)
// ObjectMapper m = new ObjectMapper();
// BigDecimal bd = m.treeToValue(node.get("a"), BigDecimal.class);
JsonNode value = node.path("a");
if (value.isNumber) {
bd = value.decimalValue();
} else {
// throw exception?
}
TestClass testClass = new TestClass();
testClass.setA(bd);
return testClass;
} which would avoid couple of conversions and eliminate sources of errors. |
@CowboyCodingRules thanks, hugs are always great but I think I am good now :) |
@cowtowncoder I have a hug for you from the OSS community. Here is a code which creates For small numbers you will get ~2x speed up and for big numbers with 1000000 digits speed up will be ~50x times. Fill free to adopt it in core of Jackson or just ask for help with a PR. |
@plokhotnyuk that is indeed interesting! I'll add it to a long list of things I'd like to work on next (https://github.com/FasterXML/jackson-future-ideas/wiki/Jackson-Work-in-Progress) |
This was a fun-to-read thread... :) @GedMarc Thank you for the hints on how to fix the issue! |
@plokhotnyuk Created FasterXML/jackson-core#577 as placeholder, hoping someone had time and interest to look into merging optimizatons. |
@cowtowncoder, is any workaround for version 2.10.1? |
@over64 Workaround for what exactly? As @GedMarc points out, use
if you simply want |
Closing this as general issue: does not mean there could not be room for improvement but it would need to be something that:
|
@cowtowncoder, yes, it parsed as big decimal, but result is so weird?
|
@over64 mapper = mapper.setNodeFactory(JsonNodeFactory.withExactBigDecimals(true)); |
@petrdvorak, thanks a lot! |
Having the below two lines(together) resolved my issue.
|
This thread saved my day ! |
What a helpful old thread!! |
Excellent old thread!! :) Thank you for help. |
Using jackson-databind 2.9.6
There seems to be an issue parsing BigDecimals as JsonNodes where it is forced to pass though an intermediate phase as a double and precision is destroyed
The code below illustrates the issue - on the original BigDecimal the precision is 2, after deserializing the precision is 1. If the custom Deserializer is disabled then the test passes.
The text was updated successfully, but these errors were encountered: