1

Trying to achieve keep the 4 decimal point formatting when deserializing from json. Right now it only gets one decimal point e.g. 1800.0. This is the json string that needs deserializing:

{
  "root": [
    {
      "paymentType": [
        {
          "listPrice": 1800.0000
        }
      ]
    }
  ]
}

I have these classes:

public class Root {
  ...
  public List<Package> package;
}

And Package

public class Package {
  ...
  public List<PaymentType> paymentType;
}

And PaymentType

public class PaymentType {
  ...
  public Double listPrice;
}

I want to format the listPrice to keep it's four decimal point. It's truncated down to .0 now.

The gson serialization code:

GsonBuilder gsonBuilder = new GsonBuilder();

gsonBuilder.registerTypeAdapter(new TypeToken<Double>() {}.getType(), new DoubleDeserializer());
Gson gson = gsonBuilder.create();
Root root = gson.fromJson(rawJson, Root.class);

DoubleDeserializer looks like:

public class DoubleDeserializer implements JsonDeserializer<Double> {
  @Override
  public Double deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
      throws JsonParseException {
    return json.getAsBigDecimal().setScale(4, RoundingMode.DOWN).doubleValue();
  }
}

Seems to me that the serialization doesn't apply to nested objects, but I could be wrong. Do I need to create the deserializer for the PaymentType class and format the listPrice there?

Update: I tried deserialize PaymentType class instead:

public PaymentType deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
      throws JsonParseException {
    JsonObject paymentType = json.getAsJsonObject();
    if (isValid(paymentType)) {
      double listPrice = paymentType.get("listPrice").getAsDouble();
      DecimalFormat df = (DecimalFormat) NumberFormat.getNumberInstance(Locale.ROOT);
      df.applyPattern("#0.0000");
      double formattedListPrice = Double.parseDouble(df.format(listPrice));
      PaymentType formattedPaymentType = new PaymentType();
      formattedPaymentType.listPrice = formattedListPrice;
      return formattedPaymentType;
    }
    return new Gson().fromJson(json, PaymentType.class);
  }

But getting the same results: 1800.0. This is due to the conversion done in the getAsDouble(), or any other parse method from json value 1800.0000. When the formatting is applied, the value is 1800.0 from the start, which makes the formatting redundant. Any tip how to combat this?

Final update: Solved (thanks @JoopEggen) by changing the Double type to BigDecimal type for listPrice in PaymentType and format using:

 BigDecimal formattedListPrice =
          paymentType.get("listPrice").getAsBigDecimal().setScale(4, RoundingMode.CEILING);

Thank you!

Addibro
  • 107
  • 1
  • 8
  • Does this answer your question? [Double decimal formatting in Java](https://stackoverflow.com/questions/12806278/double-decimal-formatting-in-java) – jhamon Oct 09 '20 at 09:02
  • 1
    A double with no trailling 0 is the same as a double with an infinite amount of trailling 0. Mathematical representation is not the same as string (human readable) representation – jhamon Oct 09 '20 at 09:03
  • Thanks for the quick response. You mean setting the scale to 4 decimal points will still only show one 0, if there are multiple trailing 0s? – Addibro Oct 09 '20 at 09:16
  • 1
    I mean there is no reason to set the scale and then get the double value as Double doesn't have a scale parameter, so the information is lost in the convertion. Mathematicaly, 1800.0 is equal to 1800.00 and to 1800.0000 or even 1800.0000000000 – jhamon Oct 09 '20 at 09:24
  • Right, the formatting works when changing the decimal points to #0.0001. But shouldn't it be still be possible to render out #0.0000? – Addibro Oct 09 '20 at 09:55
  • @fluffy thanks for the response. Fixed the json for ya. I changed to deserialize PaymentType and formatting listPrice there using your suggested solution, but the formatting is still only showing 1800.0. The formatting only works when I change the listPrice value to something like 1800.0021. – Addibro Oct 09 '20 at 11:19
  • 1
    The focus on text format deserves a warning. Whatever is texted in json, a text 1.2345 is probably _not_ that value but an approximation with a small approximation error. As floating point has no precision, just switch to the terribly verbose BigDecimal. – Joop Eggen Oct 09 '20 at 11:55
  • Switching to BigDecimal type for listPrice in PaymentType? – Addibro Oct 09 '20 at 12:04
  • 1
    Yes, instead of Double/double. And use no double constructor but `new BigDecimal("1.8000")` to provide a precision. – Joop Eggen Oct 09 '20 at 13:00
  • @JoopEggen it may sound like a bad advice. The fact that `BigDecimal` can preserve the arbitrary precision notation from input (essentially formatting), does not necessarily mean that it has to be used instead of float/double because of memory footprint and bad JSON design. Moreover, Gson supports reading numbers as strings preserving any input it has (if I remember correctly, even scientific notation when some switches are on). Additionally, in my deleted comment I suggested the OP to format the values on use site based on user's preferences but they claim `DecimalFormat` is not working. – terrorrussia-keeps-killing Oct 11 '20 at 07:19
  • In short, I would never encourage using values that are not normalized, and would never encourage adapt my code to external app quirks -- I would fix it locally unless I can tell those guys to fix the value payload. What if the value would be `"180000.00%"`: would that go across my application layers just because the external system didn't bother to serialize normalized values for _merely 1800_? Data and views, how users "see" them, are just different things. – terrorrussia-keeps-killing Oct 11 '20 at 07:26
  • @fluffy normalisation of floating point (in json) is one thing but then I had the idea that the OP tended to read more in the representation than the actual floating point could do: precission. If I were certain I would have made it an answer. So I can understand your point of view. – Joop Eggen Oct 12 '20 at 05:45

0 Answers0