I am currently developing with a 3rd party REST API. I am creating objects for their JSON objects being returned by the API. I am following their documentation where it says certain variables will be Strings. Sometimes when there is no String, it will return False. Sometimes I'm expecting a URL and I get False.
How do I handle this?
This is the API in question https://developers.cannabisreports.com/docs/
Strain Object EXAMPLE: https://ghostbin.com/paste/kgeau
In the Strain Object, I get an exception when performing a search. Some of the results of the search have booleans instead of strings/url in the bits of code that are commented out.
Sometimes I get this
"genetics": {
"names": false,
"ucpc": false,
"link": false
}
Sometimes I could get this
"genetics": {
"names": "Blueberry x Haze",
"ucpc": "W74AFGH22Z000000000000000 x 9XVU7WJQCD000000000000000",
"link": "https:\/\/www.cannabisreports.com\/api\/v1.0\/strains\/9XVU7PTG2P000000000000000\/genetics"
}
When you're in trouble with not a very well designed API, Gson gives you some flexibility while deserializing JSON documents produced by such an API. Despite you have numerous ways to work around this issue (like JsonDeserializer
, etc), you mostly like are faced with the false-as-null issue I seem to have seen here already. You can help Gson by marking "wrong" fields:
final class Envelope {
final Genetics genetics = null;
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("genetics", genetics)
.toString();
}
}
final class Genetics {
@JsonAdapter(FalseAsNullTypeAdapterFactory.class)
final String names = null;
@JsonAdapter(FalseAsNullTypeAdapterFactory.class)
final String ucpc = null;
@JsonAdapter(FalseAsNullTypeAdapterFactory.class)
final URL link = null;
@Override
public String toString() {
return MoreObjects.toStringHelper(this)
.add("names", names)
.add("ucpc", ucpc)
.add("link", link)
.toString();
}
}
And this is how the type adapter factory implemented:
final class FalseAsNullTypeAdapterFactory
implements TypeAdapterFactory {
// No worries, Gson will instantiate it itself
private FalseAsNullTypeAdapterFactory() {
}
@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
final TypeAdapter<T> delegateAdapter = gson.getAdapter(typeToken);
return new TypeAdapter<T>() {
@Override
public void write(final JsonWriter out, final T value)
throws IOException {
delegateAdapter.write(out, value);
}
@Override
public T read(final JsonReader in)
throws IOException {
// If the next token is a boolean
if ( in.peek() == JsonToken.BOOLEAN ) {
// and it's true
if ( in.nextBoolean() ) {
// then just report an unexpected `true` literal
throw new MalformedJsonException("Expected a null value indicator as `false`. " + in);
}
// and it's false, then we assume it's a null
return null;
}
// Otherwise read the whole value as a usual
return delegateAdapter.read(in);
}
};
}
}
Once you deserialize the JSON documents you provided in the question, a toString
-ed mapping might produce something like this:
Envelope{genetics=Genetics{names=null, ucpc=null, link=null}}
Envelope{genetics=Genetics{names=Blueberry x Haze, ucpc=W74AFGH22Z000000000000000 x 9XVU7WJQCD000000000000000, link=https://www.cannabisreports.com/api/v1.0/strains/9XVU7PTG2P000000000000000/genetics}}
Since you already know you will have a boolean
or a String
, you can get a JsonPrimitive
directly.
A primitive value is either a String, a Java primitive, or a Java primitive wrapper type.
Then from the JsonPrimitive
instance, you can check if it isBoolean
.
Here is how to use it in a small snippet
public static String getURL(JsonObject json, String value){
JsonPrimitive data = json.get(value).getAsJsonPrimitive();
if(data.isBoolean()){
return "Boolean : " + data.getAsBoolean();
} else {
return "String : " + data.getAsString();
}
}
And here is a quick example to validate this logic
public static void main(String[] args) {
String data =
"{"
+ " \"element\" : {"
+ " \"a\":false,"
+ " \"b\":\"foobar\""
+ " }"
+ "}";
JsonObject json = new JsonParser().parse(data).getAsJsonObject();
JsonObject element = json.get("element").getAsJsonObject();
System.out.println(getURL(element, "a"));
System.out.println(getURL(element, "b"));
}
Boolean : false
String : foobar
The json used :
{
"element" :{
"a":false,
"b":"foobar"
}
}