1

I am trying to de-serialize the json string. I tried different API's but I didn't find the solution. Here, am trying to deserialize below json and want to read value of each field/element. Example below -

 String inputJson = "{"phone":null, "address":"underworld"}";
 LinkedTreeMap map = new Gson().fromJson(inputJson , LinkedTreeMap.class);

When I say map.containsKey("phone), it is giving as false, it means "phone" element is not present in the json string. But, this is not correct as we could see that this element is present in the input json.

Can anyone help me on any API which can give keys with value as well.

With spring boot what is the correct jackson deserialization configuration which can accept null values? Currently I am using like below -

pubic ObjectMapper objectMapper(Jckson3OjectMapperBuilder builder) {
      ObjectMapper  mapper = builder.build();
      mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
      return mapper;
   }
Pand005
  • 1,095
  • 3
  • 23
  • 53

2 Answers2

0

I've written some tests that deserialize and serialize your cases maybe this will help you

GSON always deserializes null object if you want to change, write your adapter

I use JDK1.8 and com.google.code.gson:gson:2.8.6

package pl.jac.mija.gson;

import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.google.gson.TypeAdapter;
import com.google.gson.internal.LinkedTreeMap;
import com.google.gson.internal.bind.ObjectTypeAdapter;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import org.junit.Test;
import sun.reflect.generics.reflectiveObjects.NotImplementedException;

import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

public class GsonWithNullTest {

  @Test
  public void deserializeWithNull() {
    //given
    String inputJson = "{\"phone\":null, \"address\":\"underworld\"}";
    //when
    LinkedTreeMap<String, Object> map = new Gson().fromJson(inputJson, LinkedTreeMap.class);
    boolean phone = map.containsKey("phone");
    //then
    assertEquals(true, phone);
  }

  @Test
  public void deserializeWithoutNull_V1_use_adapter() {
    //given
    String inputJson = "{\"phone\":null, \"address\":\"underworld\"}";
    //when

    Gson gson = new GsonBuilder().registerTypeAdapter(LinkedTreeMap.class, new MyAdapterSkipNull()).create();
    LinkedTreeMap<String, Object> map = gson.fromJson(inputJson, LinkedTreeMap.class);
    //then
    boolean isPhone = map.containsKey("phone");
    boolean isAddress = map.containsKey("address");
    assertEquals(false, isPhone);
    assertEquals(true, isAddress);
  }

  @Test
  public void deserializeWithoutNull_V2_use_post_filter_null() {
    //given
    String inputJson = "{\"phone\":null, \"address\":\"underworld\"}";
    //when

    Gson gson = new GsonBuilder().registerTypeAdapter(LinkedTreeMap.class, new MyAdapterSkipNull()).create();
    LinkedTreeMap<String, Object> map = new Gson().fromJson(inputJson, LinkedTreeMap.class);
    Map<String, Object> collect = map.entrySet().stream().filter(x -> x.getValue() != null).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
    //then
    boolean isPhone = collect.containsKey("phone");
    boolean isAddress = collect.containsKey("address");
    assertEquals(false, isPhone);
    assertEquals(true, isAddress);
  }

  @Test
  public void serializeWithoutNull() {
    //given
    Map<String, Object> map = new HashMap<>();
    map.put("phone", null);
    map.put("address", "underworld");
    //when
    Gson gson = new GsonBuilder().serializeNulls().create();
    String json = gson.toJson(map);
    //then
    List<String> answert = new ArrayList<>();
    answert.add("{\"address\":\"underworld\",\"phone\":null}");
    answert.add("{\"phone\":null,\"address\":\"underworld\"}");
    assertTrue(answert.contains(json));
  }

  @Test
  public void serializeWithNull() {
    //given
    Map<String, Object> map = new HashMap<>();
    map.put("phone", null);
    map.put("address", "underworld");
    //when
    Gson gson = new Gson();
    String json = gson.toJson(map);
    //then
    assertEquals("{\"address\":\"underworld\"}", json);
  }

}

class MyAdapterSkipNull extends TypeAdapter<LinkedTreeMap<String, Object>> {


  @Override
  public void write(JsonWriter out, LinkedTreeMap<String, Object> value) throws IOException {
    throw new NotImplementedException();
  }

  @Override
  public LinkedTreeMap<String, Object> read(JsonReader in) throws IOException {
    JsonToken peek = in.peek();
    if (peek == JsonToken.NULL) {
      in.nextNull();
      return null;
    }
    TypeAdapter<Object> objectTypeAdapter = ObjectTypeAdapter.FACTORY.create(new Gson(), TypeToken.get(Object.class));
    LinkedTreeMap<String, Object> map = new LinkedTreeMap<>();
    in.beginObject();
    while (in.hasNext()) {
      String key = in.nextName();
      JsonToken peek1 = in.peek();
      if (JsonToken.NULL.equals(peek1)) {
        in.skipValue(); //skip NULL
      } else {
        Object read = objectTypeAdapter.read(in);
        map.put(key, read);
      }
    }
    in.endObject();
    return map;
  }
}
DEV-Jacol
  • 567
  • 3
  • 10
  • DEV-Jacol - This works if we take inputJson directly instead passing to controller method but this didn't work for me when I pass inputJSON to Spring boot controller so I have changed the method signature from Object to String then it worked. Posted the solution which I did. Thanks – Pand005 Apr 09 '20 at 13:33
0

I have solved this problem by changing spring boot end point method argument signature from Object to String. Earlier it was Object type because of that it just ignoring keys having null values in the String. And in the controller I am checking the existence of the key as below -

public ResponseEntity<Object> validate(@RequestBody String requestBody) {
Object requestObject = new ObjectMapper().readValue(requestBody, Object.class);
LinkedTreeMap requestObjectMap = new Gson().fromJson(requestObject.toString(), LinkedTreeMap.class);
List<FieldError> fieldErrors = new ArrayList<>();
final boolean isKeyExists = requestObjectMap.containsKey("keyname");
final Object fieldValue = requestObjectMap.get(optionalField);
if (isKeyExists && (Objects.isNull(fieldValue)) {
  System.out.println("Key exists but its value is null in the input Json request");
}

// other logic

}

Pand005
  • 1,095
  • 3
  • 23
  • 53