> What steps will reproduce the problem?
1. Implement a POJO that has an enum
2. Attempt to deserialize the POJO using a bad string for the enum's value
3. See that the enum's value is set to null
> What is the expected output? What do you see instead?
I expect to see an exception (probably IOException?), but instead the value is
initialized to null.
> What version of the product are you using? On what operating system?
GSON 2.3, Android 4.4.2
> Please provide any additional information below.
Here's the relevant part of TypeAdapters.EnumTypeAdapter
public T read(JsonReader in) throws IOException {
if (in.peek() == JsonToken.NULL) {
in.nextNull();
return null;
}
return nameToConstant.get(in.nextString());
}
`nameToConstant.get()` gives null when nothing is found.
Original issue reported on code.google.com by [email protected] on 12 Nov 2014 at 2:17
It is debatable what the correct behavior is. Gson in general ignores
additional fields or input.
You can easily write a custom type adapter for your enum to assert the correct
behavior.
Original comment by inder123 on 12 Nov 2014 at 10:16
The problem is that the adapter makes no difference between an input value: null and a invalid enum value (for example a typo in the enum value). Both return null. While the first is valid, the second is invalid. Thus the adapter returns the same kind of data for both valid and invalid input. It's not possible to know if the input was valid or invalid without performing an extra additional check if the input was null in case the result is null. I don't think that is correct behavior and should not be solved by writing a custom adapter. Shouldn't it throw an IllegalArgumentException when an invalid enum value is given?
Throwing an exception will cause backward compatibility issues. If you introduce a new enum value, you dont want all existing clients to break.
Trying to understand. But how would this cause backward compatibility issues? The case I can think of were there might be a problem is when json data (newer version) containing a new enum field, would be read by a java implementation (old version) using gson. In that case this will throw an exception and not be compatible. But from the java point of view. I would call this not being forward compatible. The question is if that should be a problem. Because one can not be expected to be prepared for the future. So that should not be a limitation to fix this.
Regarding ignoring additional field, input. I would consider this more of a change of input type then an additional field. For changed fields it would also throw exceptions If a field name changes. Also what if the type changes in the json data. From String to double that would cause an exception. But what if the type changed from enum to string that should cause an exception if it gets an new value.... but it doesn't it will simply return null for all the unknown enum fields.
It also doesn't ignore the input in the case of a new/wrong enum value. It just reads it with a different value, the value null.
It can also be seen different from for example UUID reading. That would throw an IllegalArgumentException in case the UUID is incorrect. If it had the same behavior as the enum parsing it should then return null. One could argue that if UUID parsing could be a problem no UUID should be used. The same can be argued for enum field. If it might be a problem with new enum values. One should probably read strings and parse them in code, not let the gson library handle it.
In general the gson library should not make the decision to interpret the input different then expected, but let the user of the library handle it and thus throw an IllegalArgumentException in this case.
You can always install your custom type adapter for your enum that enforces this. Gson isn't going to support this, sorry.
Too bad that there are no option to decide to throw an exception in that case.
If someone add an extra enum value, there won't be an issue with the existing json which don't use those value.
I agree it would be great to make the behavior configurable - both globally and on enum level (via annotation).
On enum level there could be 3 options:
nullI also support this as a configurable behavior.
Ok, it has been on a todo list for me as well.
please present a concrete proposal if you can. thanks
How about something like this:
@SerializedName(defaultValue = TrafficLight.RED)
public enum TrafficLight {
RED, ORANGE, GREEN
}
// defaultValue will also allowed to be null
// if this annotation is not present, enum maintains current behavior.
Comments @JakeWharton @swankjesse @joel-leitch?
^^
Most helpful comment
How about something like this:
@SerializedName(defaultValue = TrafficLight.RED)
public enum TrafficLight {
RED, ORANGE, GREEN
}
// defaultValue will also allowed to be null
// if this annotation is not present, enum maintains current behavior.
Comments @JakeWharton @swankjesse @joel-leitch?