Skip to content
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

BigDecimal precision not retained for polymorphic deserialization #2644

Closed
rost5000 opened this issue Mar 6, 2020 · 6 comments
Closed

BigDecimal precision not retained for polymorphic deserialization #2644

rost5000 opened this issue Mar 6, 2020 · 6 comments
Milestone

Comments

@rost5000
Copy link

rost5000 commented Mar 6, 2020

I used the gradle project, using implementation:

implementation(
            "com.fasterxml.jackson.core:jackson-annotations:2.10.3",
            "com.fasterxml.jackson.core:jackson-core:2.10.3",
            "com.fasterxml.jackson.core:jackson-databind:2.10.3",
)

A simple test have failed:

public class JacksonTest {
    @Test
    public void doTest() throws IOException {
        ObjectMapper mapper = new ObjectMapper();
        mapper.registerSubtypes(NodeParent.class);


        HttpRestClientTest.NodeRoot root = mapper.readValue(
                "{\"type\": \"NodeParent\",\"node\": {\"amount\": 9999999999999999.99} }",
                NodeRoot.class
        );

        assertEquals(new BigDecimal("9999999999999999.99"), root.getNode().getVal());

    }


    public static class NodeRoot {
        private String type;
        private Node node;

        @Override
        public String toString() {
            return "NodeRoot{" +
                    "node=" + node +
                    '}';
        }

        public String getType() {
            return type;
        }

        public void setType(String type) {
            this.type = type;
        }

        public Node getNode() {
            return node;
        }

        @JsonTypeInfo(use = JsonTypeInfo.Id.NAME, include = JsonTypeInfo.As.EXTERNAL_PROPERTY, property = "type")
        @JsonSubTypes(value = {
                @JsonSubTypes.Type(value = NodeParent.class, name = "NodeParent")
        })
        public void setNode(Node node) {
            this.node = node;
        }
    }


    @JsonIgnoreProperties(ignoreUnknown = true)
    public static class NodeParent extends Node {

        @Override
        public String toString() {
            return "NodeParent{" +
                    ", val=" + val +
                    '}';
        }
    }

    @JsonIgnoreProperties(ignoreUnknown = true)
    public static abstract class Node {
        @JsonProperty("amount")
        BigDecimal val;

        public BigDecimal getVal() {
            return val;
        }

        public void setVal(BigDecimal val) {
            this.val = val;
        }

        @Override
        public String toString() {
            return "Node{" +
                    "val=" + val +
                    '}';
        }
    }
}

The output is expected: <9999999999999999.99> but was: <1.0E+16>

What should I do?

@rost5000 rost5000 changed the title Jackson rounding up big decimal numbers for children classes Jackson rounding up big decimal numbers for children's classes Mar 6, 2020
@cowtowncoder
Copy link
Member

This sounds like a bug, from quick look. Thank you for providing reproduction!

One thing you can try is to enable setting DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS and see if that makes a difference.
It should not be needed here (since property is clearly typed as BigDecimal) but it is relevant for conversions and coercions that would happen in other cases so maybe worth checking as workaround.

@cowtowncoder
Copy link
Member

Hmmh. Probably related to buffering needed for polymorphic types.

@cowtowncoder
Copy link
Member

Ah. Ok, yes, DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS does actually prevent this.
The problem is due to buffering needed to support JsonTypeInfo.As.EXTERNAL_PROPERTY (as well as in other cases where location of type id not guaranteed to be before value).
JSON being textual format, parser does not really have concept of what accuracy is needed for floating-point values, and for buffering, for now, has to decide how to decode it before knowing how value is to be used. Double is quite a bit faster to process than BigDecimal and this is the default.

I will add a failing test and perhaps some time in future handling can be improved to defer decoding, to allow full precision even in cases like this. But until then I think you should just enable feature I mentioned and retain full accuracy.

cowtowncoder added a commit that referenced this issue Mar 6, 2020
@cowtowncoder cowtowncoder removed the 2.10 label Mar 6, 2020
@cowtowncoder cowtowncoder changed the title Jackson rounding up big decimal numbers for children's classes BigDecimal precision not retained for polymorphic deserialization Mar 6, 2020
@rost5000
Copy link
Author

It's works. Thank you for you quick replaying)

@cowtowncoder
Copy link
Member

@rost5000 glad it works; thank you for reporting the issue and verifying work-around.

@cowtowncoder cowtowncoder added the 3.x Issues to be only tackled for Jackson 3.x, not 2.x label Mar 13, 2020
@cowtowncoder cowtowncoder removed the 3.x Issues to be only tackled for Jackson 3.x, not 2.x label Jul 10, 2020
@cowtowncoder cowtowncoder added this to the 2.12.0 milestone Jul 10, 2020
@cowtowncoder
Copy link
Member

Was able to figure out a way to fix this for 2.12.0.

cowtowncoder added a commit that referenced this issue Jul 10, 2020
carterkozak added a commit to carterkozak/jackson-databind that referenced this issue Dec 12, 2020
regressed handling inside polymorphic types. The TokenBuffer could
hold values as BigDecimal objects which are heavy on heap, but
critically also cannot be used to deserialize to a less precise
target, e.g. double. This is the inverse problem of FasterXML#2644 where
not enough precision was retained, and reintroduces precision loss
while buffering floating point contents.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants