4

If I have a simple object as follows:

String name;
String email;
int age;
boolean isDeveloper;

Then let's say JSON object received have values:

{"name":null,"email":null,"age":26,"isDeveloper":true}

When deserializing this JSON using GSON as default I have:

{"age":26,"isDeveloper":true}

But email field missing will cause a failure afterwards on my application so I want to add

email = null;

Serialize back to JSON and have only this field value as null. Also not ignoring any non null fields.

Other null values should not be added to the resulting JSON.

I tried deserializing with a default GSON builder then serializing with a GSON that allows null values as:

Gson gson = new GsonBuilder().serializeNulls().create();

Problem is: this will look all null/empty values from the object class and set them all

{"name":null,"email":null,"age":26,"isDeveloper":true}

How can I set email property null then serialize back to a JSON containing only this field as null without ignoring any other non null values?

I'm using gson v2.2.4

Here is example code:

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;

public class App
{
    private class UserSimple {
        public String name;
        public String email;
        public int age;
        public boolean isDeveloper;


        UserSimple(String name, String email, int age, boolean isDeveloper) {
            this.name = name;
            this.email = email;
            this.age = age;
            this.isDeveloper = isDeveloper;
        }

    }

    public static void main( String[] args )
    {
        String userJson = "{'age':26,'email':'abc@dfg.gh','isDeveloper':true,'name':null}";
        Gson gson = new GsonBuilder()
                .setPrettyPrinting()
                .disableHtmlEscaping()
                .create();


        Gson gsonBuilder = new GsonBuilder().serializeNulls().create();

        UserSimple deserializedObj = gson.fromJson(userJson, UserSimple.class);
        System.out.println("\n"+gson.toJson(deserializedObj));
        deserializedObj.email = null;


        String serializedObj = gsonBuilder.toJson(deserializedObj, UserSimple.class);
        System.out.println("\n"+serializedObj);


    }
}
Shell_Leko
  • 512
  • 5
  • 13

2 Answers2

4

You could create your own custom adapter to resolve the issue at hand :

class MyCustomTypeAdapter extends TypeAdapter<UserSimple> {
    @Override
    public void write(JsonWriter writer, UserSimple userSimple) throws IOException {
        writer.beginObject();
        if(userSimple.getName() != null){
            writer.name("name");
            writer.value(userSimple.getName());
        }

        // you want to include email even if it's null
        writer.name("email");
        writer.value(userSimple.getEmail());

        if(userSimple.getAge() != null){
            writer.name("age");
            writer.value(userSimple.getAge());
        }
        if(userSimple.getDeveloper() != null){
            writer.name("isDeveloper");
            writer.value(userSimple.getDeveloper());
        }
        writer.endObject();
    }

    public UserSimple read(JsonReader reader) throws IOException {
    // you could create your own
        return null;
    }
} 

Input :

String userJson = "{'age':null,'email':null,'isDeveloper':true,'name':'somename'}";

Output :

serializedObj :{"name":"somename","email":null,"isDeveloper":true}

Input :

String userJson = "{'age':20,'email':null,'isDeveloper':true,'name':'somename'}";

Output :

serializedObj :{"name":"somename","email":null,"age":20,"isDeveloper":true}

Have a look at https://google.github.io/gson/apidocs/com/google/gson/TypeAdapter.html

mnj11
  • 111
  • 3
  • Do not forget to register your adapter like so : Gson gsonBuilder = new GsonBuilder().registerTypeAdapter(UserSimple.class, new MyCustomTypeAdapter()).serializeNulls().create(); – mnj11 Jul 27 '18 at 23:58
  • What if values aren't so simple? There is an object inside it, let's say: "isDeveloper {"java": true, "python":false}" how can I reproduce this structure with this solution? – Shell_Leko Jul 31 '18 at 17:16
  • I suppose in that case, you could define TypeAdapter instances for your nested objects and use the "getAdapter" method accordingly to set the correct values. – mnj11 Aug 01 '18 at 02:40
1

Have you considered using jackson? I think it's more powerful and faster than Gson. But that's just my opinion, I don't mean to offend anyone. I've also run into situations where jackson could do something that gson couldn't.

Here's the code that does the job for you:

import com.fasterxml.jackson.annotation.JsonInclude;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.ObjectWriter;

public class Main {
    static class Person {
        @JsonInclude(JsonInclude.Include.ALWAYS)
        private String name;
        @JsonInclude(JsonInclude.Include.NON_NULL)
        private String email;
        @JsonInclude(JsonInclude.Include.NON_NULL)
        private Integer age;
        public Person(String name, String email, Integer age) {
            this.name = name;
            this.email = email;
            this.age = age;
        }

        public String getName() {
            return name;
        }

        public String getEmail() {
            return email;
        }

        public Integer getAge() {
            return age;
        }
    }
    public static void main(String[] args) throws Exception {
        ObjectWriter ow = new ObjectMapper().writer().withDefaultPrettyPrinter(); // pretty printing only enabled for demonstration purposes
        String test = ow.writeValueAsString(new Person(null, null, 2));
        System.out.println(test);
    }
}

output is:

{
  "name" : null,
  "age" : 2
}

name is also output because the inclusion policy for it is JsonInclude.Include.ALWAYS.

In maven dependencies include:

<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.9.6</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>2.9.6</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.9.6</version>
</dependency>

There's also this answer: Gson serialize null for specific class or field but it looks like it's not very good.

Coder-Man
  • 2,391
  • 3
  • 11
  • 19
  • Sure you are not offending anyone, but unfortunately using GSON is a requirement that cant be changed. Although I reckon your answer will be much appreciated for those using jackson that are facing the same problems. Thanks! – Shell_Leko Jul 30 '18 at 13:33