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':'[email protected]','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);


    }
}

2 Answers 2

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

Sign up to request clarification or add additional context in comments.

3 Comments

Do not forget to register your adapter like so : Gson gsonBuilder = new GsonBuilder().registerTypeAdapter(UserSimple.class, new MyCustomTypeAdapter()).serializeNulls().create();
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?
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.
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.

1 Comment

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!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.