Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I retain the default values of field in a deserialized object?

Tags:

java

json

gson

My situation is as follows: Object TableC has 4 fields. Only 3 fields (field_C1, field_C2, and field_C3) are read from the JSON string. The fourth field field_C4 is defined within the object with a default value.

When I serialize the object instance (for output) - it ignores the field field_C4, I was expecting the default value of "1" or "null". When I explicitly define a value to the instance-field within the program to "NEW", it does include it in the Json output string.

Looking at the output, it looks as if the constructor is also ignored when the object instance is created during deserialization.

What would be the best practice to activate the other fields for the object instance - which are not included in the Deserialized version of the input Json String?

package newpackage;
import java.util.List;

import com.google.gson.*;


public class jsonwithconstructor {

   public static void main(String[] args) throws ClassNotFoundException {

    String jsonstring = "{'TableC':["
            + "{'field_C1':'C_11','field_C2':'C_12','field_C3':'C_13'},"
            + "{'field_C1':'C_21','field_C2':'C_22','field_C3':'C_23'}"
            + "]}";

    jsonstring = jsonstring.replace('\'', '"');
    System.out.println(jsonstring); 

    RootObject root = new GsonBuilder().create().fromJson(jsonstring, RootObject.class);

    for (int i=0; i < root.TableC.size(); i++){
        System.out.println(root.TableC.get(i));
    }

    System.out.println();

    //root.TableC.get(0).field_C4 = "NEW";

    for (int i=0; i < root.TableC.size(); i++){
        System.out.println(root.TableC.get(i));
    }
    System.out.println();

    Gson gson = new Gson();

    String jsonoutput = gson.toJson(root);
    System.out.println(jsonoutput); 


}

public class TableC{
    public String field_C1;
    public String field_C2;
    public String field_C3;
    public String field_C4 = "1";

    public TableC(){
        this.field_C4 = "1";
    }

    @Override
    public String toString() {
        return ("TableC" + ", " + this.field_C1 + ", " + this.field_C2 + ", " + this.field_C3 + ", " + this.field_C4);
    }
}
public class RootObject{

    public List<TableC> TableC; 

}

}

The output is shown below:

{"TableC":[{"field_C1":"C_11","field_C2":"C_12","field_C3":"C_13"},{"field_C1":"C_21","field_C2":"C_22","field_C3":"C_23"}]}
TableC, C_11, C_12, C_13, null
TableC, C_21, C_22, C_23, null

TableC, C_11, C_12, C_13, NEW
TableC, C_21, C_22, C_23, null

{"TableC":[{"field_C1":"C_11","field_C2":"C_12","field_C3":"C_13","field_C4":"NEW"},{"field_C1":"C_21","field_C2":"C_22","field_C3":"C_23"}]}
like image 308
userDSSR Avatar asked Sep 10 '15 20:09

userDSSR


2 Answers

This is a known, currently open issue: https://github.com/google/gson/issues/513

Gson constructs the values of fields in deserialized objects with reflection, so it will set the values based on what's in the JSON only. Until Google provides a fix for this issue, there isn't that much you can do.

There are a number of workaround objects you have in the meantime:

  1. Wrap the fields in getters, and lazily load the value. This is a good way (and my personal recommendation) to do it if a field is never ever allowed to be null, but they do need to be mutable.
  2. Mark the default fields as final. This a good way to do it if they are immutable.
  3. Create a custom ExclusionStrategy, and mark the particular fields that should be ignored using FieldAttributes
    • This is the most versatile of the options but also the most code.
  4. Deserialize your POJO using just the fields that don't exist, and then compose that data structure with a new one that has the default values.

I agree that all of these have drawbacks, but as I said above, this is an open issue with Gson.

like image 157
durron597 Avatar answered Oct 04 '22 08:10

durron597


Using Gson v2.7, I can see that it calls the default constructor of the deserialized object and any value initialized inside that is retained. However, I was using "new Gson()" instead of "GsonBuilder", if that makes any difference.

like image 30
ravindu1024 Avatar answered Oct 04 '22 09:10

ravindu1024