Inside Data Structure of API Responses, Variables of Enum Type should be Nullable
This article is a quick and straightforward note for API compat that introduces how we made a mistake when we can’t easily extend our initial API response.
Imagine that we have an API which replies the product’s shipping and payments’ types:
{
"id": 7512115,
"shipping": {
"type": 2,
"value": "3"
},
"payments":[
1,
2,
3,
5
]
}
We might simply define the following data structure for this API response. We didn’t add @Nullable
because there must be ShippingType type
and others inside API responses.
public class Product {
public String id;
public Shipping shipping;
public List<Payment> Payments;
}public class Shipping {
public ShippingType type;
public String value;
}public enum ShippingType {
@SerializedName("0")
NORMAL,
@SerializedName("1")
PREORDER,
@SerializedName("2")
CUSTOMMADE,
@SerializedName("3")
SELFBOOK
}public enum Payment {
@SerializedName(value = "1")
ATM,
@SerializedName(value = "2")
CREDIT_CARD,
@SerializedName(value = "3")
LINE_PAY,
@SerializedName(value = "4")
APPLE_PAY,
@SerializedName(value = "5")
GOOGLE_PAY
}
It works well until this API has a new type of “4”, which didn’t support in original ShippingType, so we might think it’s easy to modify.
public enum ShippingType {
@SerializedName("0")
NORMAL,
@SerializedName("1")
PREORDER,
@SerializedName("2")
CUSTOMMADE,
@SerializedName("3")
SELFBOOK,
@SerializedName("4")
FAST
}
Yes, this is correct implementation, but only the new version of the app. The previous version might crash because it didn’t recognize “4”, so it will have ShippingType type
of a null value. It quickly causes a RuntimeException, for example,
if (product == null || product.shipping == null) {
return;
}switch(product.shipping.type) {
case NORMAL:
// TODO
case PREORDER:
// TODO
case CUSTOMMADE:
// TODO
case SELFBOOK:
// TODO
default:
// DO NOTHING
}
It will crash at switch(product.shipping.type)
because type
is null and switch
will try to get type’s enum ordinal()
.
List<Payment> Payments
might also occur crash when new payment type from API, but the app didn’t define either. In this case, the Payments list will contain a null element, and then we easily forget to verify, especially on foreach
.
There are so many scenarios to improve this implementation, for example, add the annotation
public class Shipping {
@Nullable public ShippingType type;
@NonNull public String value;
}
So you won’t forget to verify type
before switch
. Or you can use Kotlin
data class Shipping (
val type : ShippingType?
val value : String
)
Gson will write unknown
enum
values asnull
— ignoring the Kotlin nullability — since the Java reflection doesn’t know about Kotlin. Make such values nullable and handle them as deserialization errors or use Moshi which will do it automatically.
The most important is that, when using Enum for API responses, we have to make our app work even new value it cannot recognize in the further.