Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Gson optional and required fields

Tags:

json

gson

How should one deal with Gsonand required versus optional fields?

Since all fields are optional, I can't really fail my network request based on if the response json contains some key, Gsonwill simply parse it to null.

Method I am using gson.fromJson(json, mClassOfT);

For example if I have following json:

{"user_id":128591, "user_name":"TestUser"} 

And my class:

public class User {      @SerializedName("user_id")     private String mId;      @SerializedName("user_name")     private String mName;      public String getId() {         return mId;     }      public void setId(String id) {         mId = id;     }      public String getName() {         return mName;     }      public void setName(String name) {         mName = name;     } } 

Is the any option to get Gson to fail if json would not contain user_id or user_name key?

There can be many cases where you might need at least some values to be parsed and other one could be optional?

Is there any pattern or library to be used to handle this case globally?

Thanks.

like image 730
Niko Avatar asked Feb 07 '14 11:02

Niko


People also ask

Does GSON ignore extra fields?

As you can see, Gson will ignore the unknown fields and simply match the fields that it's able to.

Does GSON ignore transient fields?

By default, GSON excludes transient and static fields from the serialization/deserialization process.

Is GSON better than JSON?

GSON can use the Object definition to directly create an object of the desired type. While JSONObject needs to be parsed manually.


1 Answers

As you note, Gson has no facility to define a "required field" and you'll just get null in your deserialized object if something is missing in the JSON.

Here's a re-usable deserializer and annotation that will do this. The limitation is that if the POJO required a custom deserializer as-is, you'd have to go a little further and either pass in a Gson object in the constructor to deserialize to object itself or move the annotation checking out into a separate method and use it in your deserializer. You could also improve on the exception handling by creating your own exception and pass it to the JsonParseException so it can be detected via getCause() in the caller.

That all said, in the vast majority of cases, this will work:

public class App {      public static void main(String[] args)     {         Gson gson =             new GsonBuilder()             .registerTypeAdapter(TestAnnotationBean.class, new AnnotatedDeserializer<TestAnnotationBean>())             .create();          String json = "{\"foo\":\"This is foo\",\"bar\":\"this is bar\"}";         TestAnnotationBean tab = gson.fromJson(json, TestAnnotationBean.class);         System.out.println(tab.foo);         System.out.println(tab.bar);          json = "{\"foo\":\"This is foo\"}";         tab = gson.fromJson(json, TestAnnotationBean.class);         System.out.println(tab.foo);         System.out.println(tab.bar);          json = "{\"bar\":\"This is bar\"}";         tab = gson.fromJson(json, TestAnnotationBean.class);         System.out.println(tab.foo);         System.out.println(tab.bar);     } }  @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) @interface JsonRequired { }  class TestAnnotationBean {     @JsonRequired public String foo;     public String bar; }  class AnnotatedDeserializer<T> implements JsonDeserializer<T> {      public T deserialize(JsonElement je, Type type, JsonDeserializationContext jdc) throws JsonParseException     {         T pojo = new Gson().fromJson(je, type);          Field[] fields = pojo.getClass().getDeclaredFields();         for (Field f : fields)         {             if (f.getAnnotation(JsonRequired.class) != null)             {                 try                 {                     f.setAccessible(true);                     if (f.get(pojo) == null)                     {                         throw new JsonParseException("Missing field in JSON: " + f.getName());                     }                 }                 catch (IllegalArgumentException ex)                 {                     Logger.getLogger(AnnotatedDeserializer.class.getName()).log(Level.SEVERE, null, ex);                 }                 catch (IllegalAccessException ex)                 {                     Logger.getLogger(AnnotatedDeserializer.class.getName()).log(Level.SEVERE, null, ex);                 }             }         }         return pojo;      } } 

Output:

 This is foo this is bar This is foo null Exception in thread "main" com.google.gson.JsonParseException: Missing field in JSON: foo 
like image 193
Brian Roach Avatar answered Sep 18 '22 19:09

Brian Roach