roger_that
roger_that

Reputation: 9791

Return BigDecimal fields as JSON String values in Java

I have a Get API which returns a JSON. The format expected is:

{
   "value1": "123.00",
   "value2": "23.00"
}

These values are BigDecimal in the object:

public class Result{
    BigDecimal value1;
    BigDecimal value2;
    // getters and setters
}

Now, when the request is made, I create the Result object and populate these values as BigDecimal, create a list and return. The response I received is:

{
   "value1": 123.00,
   "value2": 23.00
}

The values are correct but are not strings. How can I make them strings?

I know we can convert BigDecimal to String but I have object with BigDecimal fields only and I can't populate String values to it.

Finally, this is how the list is made:

List<MyObj> obj = Arrays.stream(myObj).collect(Collectors.toList());

    Result result = new Result();

    obj.forEach(t -> t.mergeToResult(result));  // mergeToResults does all the population like result.setValue1(new BigDecimal(123.00))

    return result;

Upvotes: 10

Views: 42011

Answers (3)

Serhii Maksymchuk
Serhii Maksymchuk

Reputation: 5424

If you use spring framework, you can configure serialization BigDecimal -> String globally like this:

@Bean
@Primary
ObjectMapper objectMapper() {
    ObjectMapper objectMapper = new ObjectMapper();
    objectMapper.configOverride(BigDecimal.class)
            .setFormat(JsonFormat.Value.forShape(JsonFormat.Shape.STRING));
    return objectMapper;
}

Upvotes: 1

Pawan Kumar
Pawan Kumar

Reputation: 299

You can use @JsonFormat annotation as below:

import com.fasterxml.jackson.annotation.JsonFormat;

public class Result {        
   @JsonFormat(shape = JsonFormat.Shape.STRING)
   private BigDecimal value;

   // getters and setters
}

Upvotes: 8

Basil Bourque
Basil Bourque

Reputation: 338574

Use String

The Number type in JSON is likely to be considered as a floating-point number, from my reading of the Wikipedia page, per the traditional behavior of JavaScript. Floating-point technology purposely sacrifices accuracy for the sake of execution speed.

The BigDecimal class has exactly the opposite purpose, to sacrifice execution speed for the sake of accuracy.

So, if you want the best chance of preserving your BigDecimal value, store as a String. Hopefully this approach makes it less likely that the consumer of your JSON will inadvertently parse the incoming value as a floating-point. Clearly document to those people consuming your JSON about your intention to have the data parsed as a BigDecimal or equivalent rather than floating-point.

Java export:

String output = myBigDecimal.toString() ;

JSON:

{
   "value1": "123.00",
   "value2": "23.00"
}

Java import:

BigDecimal bd = new BigDecimal( "123.00" ) ;

Study the documentation for BigDecimal::toString and new BigDecimal( string ). You should also be aware of the BigDecimal::toPlainString method.

Streams

As for your Java streams, I do not understand your issue. You use of List and arrays is unexplained.

Basically, you should be implementing a toJson method on your Result class. Call that Result::toJson method within your stream code.

Here is an example of such a class.

package com.basilbourque.example;

import java.math.BigDecimal;

public class Result {

    BigDecimal start, stop;

    public String toJson () {
        StringBuilder sb = new StringBuilder( "{\n" );
        sb.append( "    " );  // Indent with four spaces.
        sb.append( this.enquote( "start:" ) + " " );
        sb.append( this.enquote( start.toString() ) );
        sb.append( " , \n" );
        sb.append( "    " );  // Indent with four spaces.
        sb.append( this.enquote( "stop:" ) + " " );
        sb.append( this.enquote( stop.toString() ) );
        sb.append( "\n}\n" );
        return sb.toString();
    }

    static public Result fromJson ( String json ) {
         …
    }

    private String enquote ( String string ) {
        return "\"" + string + "\"";
    }

    // Constructor
    public Result ( BigDecimal start , BigDecimal stop ) {
        this.start = start;
        this.stop = stop;
    }

    public static void main ( String[] args ) {
        Result r = new Result( new BigDecimal( "123.00" ) , new BigDecimal( "23.00" ) );
        System.out.println( r.toJson() );
    }
}

When run.

{
    "start:" "123.00" , 
    "stop:" "23.00"
}

Consider a framework

Tip: You may want to consider using a JSON↔Java mapping framework to help you with this work.

Upvotes: 3

Related Questions