Paul Burke
Paul Burke

Reputation: 25584

NumberFormatException in GSON when converting String to double

I am working with a JSON response that is improperly formatted. All fields are being returned as Strings. Unfortunately, I have no control over the return data.

I am using Gson and attempting to parse a JSON object that includes a field like this:

{
        [...]
    "cost": "9.25"
}

It should obviously be printed as a Number. When I try to parse this as a String, Number or double I get a NumberFormatException:

com.google.gson.JsonSyntaxException: java.lang.NumberFormatException: 
        [...]
    at com.myapp.android.LauncherActivity$1.onSuccess(LauncherActivity.java:69)
        [...]
Caused by: java.lang.NumberFormatException: 
    at org.apache.harmony.luni.util.FloatingPointParser.parseDouble(FloatingPointParser.java:267)
    at java.lang.Double.parseDouble(Double.java:285)
    at com.google.gson.stream.JsonReader.nextInt(JsonReader.java:599)
    at com.google.gson.internal.bind.TypeAdapters$7.read(TypeAdapters.java:228)
    ... 19 more

LauncherActivity Line 69:

Item item = gson.fromJson(response, Item.class);

So I followed this similar question and tried creating a TypeAdapter like so:

public class CostTypeAdapter implements JsonDeserializer<Double>, JsonSerializer<Double> {

    public Double deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        Double cost;
        try {
            cost = json.getAsDouble();
        } catch (NumberFormatException e) {
            cost = 0.00d;
        }
        return cost;
    }

    public JsonElement serialize(Double src, Type typeOfSrc, 
            JsonSerializationContext context) {
        return new JsonPrimitive(src);
    }
}

And registered it when creating the GsonBuilder:

GsonBuilder builder = new GsonBuilder();
builder.registerTypeAdapter(Cost.class, new CostTypeAdapter());
Gson gson = builder.create(); 

And my Cost class:

public class Cost {
    private Double value;

    public Cost(Double value) {
        this.value = value;
    }

    public Double getValue() {
        return value;
    }
}

But I get the same NumberFormatException.

Any ideas on whats happening here? Shouldn't this exception be caught in my CostTypeAdapter.deserialize(), at the very least?

Any help/guidance is greatly appreciated.

Upvotes: 5

Views: 23700

Answers (6)

MohamedHarmoush
MohamedHarmoush

Reputation: 1352

the easiest solution is to make the attribute to be String instead of double and in the get method parse it to double.

for example:

class Location {
 private String latitude;
 private String longitude;

 public double getLatitude() {
        return latitude.isEmpty() ? 0d : Double.parseDouble(latitude);
    }

    public double getLongitude() {
        return longitude.isEmpty() ? 0d : Double.parseDouble(longitude);
    }
}

Upvotes: 0

Sandeep Kumar
Sandeep Kumar

Reputation: 2557

I also ran into a similar situation. I used below adaptor for the conversion. I Found it concise.

.registerTypeAdapter(Double.class, new JsonSerializer<Double>() {
            public JsonElement serialize(Double number, Type type, JsonSerializationContext context) {
                return new JsonPrimitive(Double.valueOf(number));
            }
        })

Upvotes: 0

Dandre Allison
Dandre Allison

Reputation: 6035

Paul, I have a similar issue of getting numbers in my JSON that are stored as Strings. What's been working for me is this:

public enum Plan {
        GUEST_PASS, FREE, PREMIUM;

        static Plan fromValue(int value) {
            for (Plan plan : plans)
                if (value == plan.ordinal())
                    return plan;
            throw new IllegalArgumentException("Invalid value for Plan: " + value);
        }

        static Plan fromValue(String string) {
            try {
                return fromValue(parseInt(string));
            } catch (IllegalArgumentException _) {
                throw new IllegalArgumentException("Invalid value for Plan: " + string);
            }
        }

        private static EnumSet<Plan> plans = EnumSet.allOf(Plan.class);
    }

public static class PlanAdapter extends TypeAdapter<Plan> {
    @Override public void write(JsonWriter json, Plan plan) throws IOException {
        json.value(Integer.toString(plan.ordinal()));
    }

    @Override public Plan read(JsonReader json) throws IOException {
        return Plan.fromValue(json.nextString());
    }
}

Seems like you have continuous data in your case, so you would suffice to have a class, I converted to an enum since I had discrete data.

Upvotes: 0

pandre
pandre

Reputation: 6725

You can also use GsonBuilder's registerTypeAdapter() to catch possible parsing Exceptions and deal with them the way you want.

Example for catching NumberFormatException when parsing Float and make the value null:

    GsonBuilder gb = new GsonBuilder();
    gb.registerTypeAdapter(Float.class, new TypeAdapter<Float>() {

        @Override
        public Float read(JsonReader reader) throws IOException {
            if (reader.peek() == JsonToken.NULL) {
                reader.nextNull();
                return null;
            }
            String stringValue = reader.nextString();
            try {
                Float value = Float.valueOf(stringValue);
                return value;
            } catch (NumberFormatException e) {
                return null;
            }
        }

        @Override
        public void write(JsonWriter writer, Float value) throws IOException {
            if (value == null) {
                writer.nullValue();
                return;
            }
            writer.value(value);
        }

    });

Upvotes: 11

Jesse Wilson
Jesse Wilson

Reputation: 40613

It looks like the cost field in your Item class is declared as an int, not as a double. Changing cost to a double should fix it.

Upvotes: 0

Paul Burke
Paul Burke

Reputation: 25584

I ended up having to write a JsonDeserializer for my entire enclosing "Item" class.

public class ItemDeserializer implements JsonDeserializer<Item> {

    @Override
    public Item deserialize(JsonElement json, Type type,
            JsonDeserializationContext context) throws JsonParseException {

        JsonObject jobject = (JsonObject) json;

        return new Item(
                    [...],
                (jobject.has("cost")) ? jobject.get("cost").getAsDouble() : 0.00d
                );
    }
}

Would still love to see a solution for my original issue, so I wouldn't have to manually parse every field.

Upvotes: 2

Related Questions