20

When I use Gson to serialize an Object that contains a double value close to zero it is using the scientific E-notation:

{"doublevaule":5.6E-4}

How do I tell Gson to generate

{"doublevaule":0.00056}

instead? I can implement a custom JsonSerializer, but it returns a JsonElement. I would have to return a JsonPrimitive containing a double having no control about how that is serialized.

Thanks!

Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
konstantin
  • 695
  • 2
  • 6
  • 14
  • 5
    Why is that a problem? Scientific notation is valid in JSON and anything handling JSON should be able to parse that correctly (to exactly the same value as if scientific notation were not used). – Joachim Sauer Jun 20 '12 at 12:03
  • 1
    I don't argue with that, you are right Joachim. Still I would like my JSON to not contain scientific notations. Is that doable with GSon? – konstantin Jun 20 '12 at 12:14
  • I have exactly the same problem, simply due to the fact I have a broken JSON consumer that can't handle exponents. I realise the *real* fix is to sort the consumer, but sometimes that's out of our control. Hence I think this is a reasonable (if unusual) requirement – Brian Agnew Sep 19 '13 at 10:56

7 Answers7

14

Why not provide a new serialiser for Double ? (You will likely have to rewrite your object to use Double instead of double).

Then in the serialiser you can convert to a BigDecimal, and play with the scale etc.

e.g.

    GsonBuilder gsonBuilder = new GsonBuilder();
    gsonBuilder.registerTypeAdapter(Double.class,  new JsonSerializer<Double>() {
        @Override
        public JsonElement serialize(final Double src, final Type typeOfSrc, final JsonSerializationContext context) {
            BigDecimal value = BigDecimal.valueOf(src);

            return new JsonPrimitive(value);
        }
    });

    gson = gsonBuilder.create();

The above will render (say) 9.166666E-6 as 0.000009166666

Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
  • With Kotlin - see my answer https://stackoverflow.com/a/57254418/2826184 – Elnatan Derech Jul 29 '19 at 13:01
  • I tried gson.toJson("{\"doublevaule\":5.6E-4}"); after above lines of code, I am still getting the value as it. i.e., out put of above line is 5.6E-4 – Parthi Sep 06 '22 at 16:46
5

A minor change on Brian Agnew's answer:

public class DoubleJsonSerializer implements JsonSerializer<Double> {
    @Override
    public JsonElement serialize(final Double src, final Type typeOfSrc, final JsonSerializationContext context) {
        BigDecimal value = BigDecimal.valueOf(src);
        try {
            value = new BigDecimal(value.toBigIntegerExact());
        } catch (ArithmeticException e) {
            // ignore
        }

        return new JsonPrimitive(value);
    }
}
Community
  • 1
  • 1
Ron Klein
  • 9,178
  • 9
  • 55
  • 88
3

Internally GSON uses Number#toString so we just need to create a new instance of a Number:

.registerTypeAdapter(Double.class, new JsonSerializer<Double>() {
    @Override
    public JsonElement serialize(final Double src, final Type typeOfSrc, final JsonSerializationContext context) {

        Number n = new Number() {

            @Override
            public long longValue() {
                return 0;
            }

            @Override
            public int intValue() {
                return 0;
            }

            @Override
            public float floatValue() {
                return 0;
            }

            @Override
            public double doubleValue() {
                return 0;
            }

            @Override
            public String toString() {
                return new BigDecimal(src).toPlainString();
            }

        };

        return new JsonPrimitive(n);
    }
})
Denis
  • 603
  • 4
  • 8
2

You could try extending JsonWriter and overriding the method value(double)

It doesn't seem to be built to be modified like this (you'll pretty much need to duplicate the existing code), but it should be possible to get it working.

Unfortunately I see no other reason to influence the output format.

Joachim Sauer
  • 302,674
  • 57
  • 556
  • 614
  • This is the right approach, but it will probably require a lot of duplicated code. JsonWriter wasn't designed for this kind of extensibility and lacks the necessary hooks. – Jesse Wilson Jun 20 '12 at 14:16
2

Create a custom serializer for Double

GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Double.class,  new JsonSerializer<Double>() {
    @Override
    public JsonElement serialize(Double originalValue, Type typeOf, JsonSerializationContext context) {
        BigDecimal bigValue = BigDecimal.valueOf(originalValue);

        return new JsonPrimitive(bigValue.toPlainString());
    }
});

Before: {"Amount": 1.0E9}

After: {"Amount": "1000000000"}

Not exactly perfect since it's a String in the JSON.

swanson
  • 7,377
  • 4
  • 32
  • 34
2

With Kotlin:

    val gsonBuilder = GsonBuilder()
    gsonBuilder.registerTypeAdapter(object: TypeToken<Double>() {}.type, object : JsonSerializer<Double> {
        override fun serialize(src: Double, typeOfSrc: Type, context: JsonSerializationContext): JsonElement {
            val value = BigDecimal.valueOf(src)
            return JsonPrimitive(value)
        }
    })

    val gson = gsonBuilder.create()
Elnatan Derech
  • 987
  • 2
  • 12
  • 18
0

For some reason I can't get the solution with BigDecimal to work as suggested here by many people. So I had to write it like that:

.registerTypeAdapter(object : TypeToken<Double>() {}.type, object : JsonSerializer<Double> {
    override fun serialize(src: Double, typeOfSrc: Type, context: JsonSerializationContext): JsonElement =
        JsonPrimitive(src.toBigDecimal().toPlainString().toBigDecimal())
})
Bogdan Kornev
  • 101
  • 1
  • 5