Deserialise JSON payload to JsonNode with custom logic

Viewed 107

I want to deserialise a String to JsonNode while converting all doubles to ints where possible. For example, with the following code

// str is = { "a": 1.0, "b": 0.5 }
var objectMapper = new ObjectMapper();
JsonNode n = objectMapper.readTree(str);

Current behavior: Both children of n are type DoubleNode.

Goal: For the children of n, a is type IntNode and b is type DoubleNode.

I've seen a lot of custom deserializers on here but they only apply to defined POJOs and not arbitrary JsonNode deserialization. Would appreciate any help on how to customize the string to JsonNode deserialization.

2 Answers

You may want to preprocess the input JSON string and remove the fractional part containing only 0s along with the fractional dot .:

String str = "{ \"a\": \"1.0\", \"b\": 0.5, \"c\":\"0.01\", \"d\": 0.00}"
  .replaceAll("(\\d+)\\.0+(\\D)", "$1$2")); // check if fractional part contains only 0's

System.out.println(str);

Here "$1$2" refer to the first (\\d+) (non-empty sequence of digits) and the second (\\D) (any non-digit character) matching groups which should be kept after the replacement.

Output:

{ "a": "1", "b": 0.5, "c":"0.01", "d": 0}

Then a and d can be parsed as integers.

If you deserialise JSON to JsonNode you can override JsonNodeFactory which creates nodes with data. Example:

JsonMapper mapper = JsonMapper.builder()
        .nodeFactory(new JsonNodeFactory(true) {
            @Override
            public NumericNode numberNode(double v) {
                if (v == (int) v) {
                    return super.numberNode((int) v);
                }
                if (v == (long) v) {
                    return super.numberNode((long) v);
                }
                return super.numberNode(v);
            }
        })
        .build();

As you can see, you can define which type of node will be created for a given value.

Similar question:

Related