Reputation: 25584
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
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
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
Reputation: 6035
Paul, I have a similar issue of getting numbers in my JSON that are stored as String
s. 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
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
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
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