GSON десериализация нулевого значения не работает - PullRequest
0 голосов
/ 04 апреля 2020

Я пытаюсь десериализовать строку json. Я пробовал разные API, но не нашел решения. Здесь я пытаюсь десериализоваться ниже json и хочу прочитать значение каждого поля / элемента. Пример ниже -

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

Когда я говорю map.containsKey ("телефон", он дает как ложный, это означает, что элемент "телефон" отсутствует в строке json. Но это не так правильно, как мы могли видеть, что этот элемент присутствует во входных данных json.

Может ли кто-нибудь помочь мне в любом API, который также может давать ключи со значением.

С пружинной загрузкой, что правильная конфигурация десериализации Джексона, которая может принимать нулевые значения? В настоящее время я использую, как показано ниже -

pubic ObjectMapper objectMapper(Jckson3OjectMapperBuilder builder) {
      ObjectMapper  mapper = builder.build();
      mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
      return mapper;
   }

Ответы [ 2 ]

0 голосов
/ 09 апреля 2020

Я решил эту проблему, изменив сигнатуру аргумента метода конечной точки пружинной загрузки с Object на String. Раньше это был тип Object, потому что он просто игнорировал ключи, имеющие нулевые значения в String. А в контроллере я проверяю наличие ключа как показано ниже -

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

}

0 голосов
/ 05 апреля 2020

Я написал несколько тестов для десериализации и сериализации ваших кейсов, может быть, это поможет вам

GSON всегда десериализует нулевой объект, если вы хотите изменить, напишите свой адаптер

Я использую JDK1. 8 и 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;
  }
}
...