0

I was build my api based on springmvc v4.3.12, weblogic12c, there is one api will return a big number, its a BigDecimal field, maybe its because the number is too big that make this strange things happend.

code:

@GetMapping(value = "test")
public ResponseEntity<Map<String, BigDecimal>> test() throws IOException {
    Map<String, BigDecimal> map = Maps.newHashMap();
    map.put("a", new BigDecimal("555511112222333.1729").setScale(4, BigDecimal.ROUND_DOWN));
    return ResponseEntity.ok(map);
}

output:

{
  "a": 555511112222333.2
}

if change the number to 5555111122223.1729, the output will be 5555111122223.173

if change the number to 555511.1729, the output will be 555511.1729

I have debugged the code of spring, it was wrote the number into servlet response buffer correctly, but in the caller side, like swagger ui, the number was lost precisions.

I also tried with springboot v2.3.2, the api returned Bigdecimal field correctly, did't lost precisions.

tim
  • 1
  • 1
  • Jackson, by default, is used to send JSON. So probably Jackson is using some default precision/length for those numbers. It could even be the `tostring` of `BigDecimal` is being used as you are serializing a `Map` instead of an object. – M. Deinum Dec 17 '21 at 10:18
  • Be aware the limit of javascript number too ... It's often shorter than java long/bigdecimal – Huy Nguyen Dec 17 '21 at 10:50
  • 1
    JSON by default uses `double` for decimal numbers (since this is what all programming languages understand) and this is what Jackson upon serializing a `BigDecimal` does by default. There are ways to tell Jackson to use custom serializers (https://stackoverflow.com/a/18226861) but then you will get `"a": "5555111122223.1729"` – Thomas Kläger Dec 17 '21 at 10:51
  • @ThomasKläger there are many people give the answer just like you said change the number to string, and they are choose to ignore the difference between a number and a string, it is not find out the root cause, just skip it. – tim Dec 18 '21 at 01:32

1 Answers1

0

I did some further research.

I used this small program to verify what output the backend produces:

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

public class JsonOutput {

    public static void main(String[] args) throws JsonProcessingException {
        Map<String, BigDecimal> map = new HashMap<>();
        map.put("a", new BigDecimal("555511112222333.17291234567890"));

        ObjectMapper om = new ObjectMapper();
        System.out.println(om.writeValueAsString(map));
    }
}

And this prints

{"a":555511112222333.17291234567890}

So the backend produces the output that you want. This conclusion is supported by the following statement from your question:

I have debugged the code of spring, it was wrote the number into servlet response buffer correctly

Therefore I re-read the second part of your statement:

but in the caller side, like swagger ui, the number was lost precisions

And in the end this makes totally sense:

The Swagger UI is a generic UI and doesn't know anything about your formatting needs. It probably just uses JSON.parse() to read the JSON that your code generated. And JSON.parse() interprets any number value as ECMAScript Number which for most purposes is basically the same as a Java double value.

And therefore my conclusion from the comment still holds:

  • If your backend code needs to control the representation of a numeric value it must not transmit it as numeric value but as a string
  • If your backend code transmits a numeric value as a numeric value then whoever reads that value will apply its formatting rules to the value
  • If you are in full control of backend and frontend you can do as you wish
Thomas Kläger
  • 17,754
  • 3
  • 23
  • 34
  • It is a bug of spring, or jackson, and i find out a solusion is that change the response content to json string then write it into servlet response. – tim Dec 20 '21 at 10:05