【问题标题】:GSON deserializing null value not workingGSON反序列化空值不起作用
【发布时间】:2020-04-04 03:41:17
【问题描述】:

我正在尝试反序列化 json 字符串。我尝试了不同的 API,但没有找到解决方案。在这里,我试图反序列化下面的 json 并想要读取每个字段/元素的值。下面的例子 -

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

当我说 map.containsKey("phone) 时,它给出的是 false,这意味着 json 字符串中不存在“phone”元素。但是,这是不正确的,因为我们可以看到这个元素存在于输入的 json。

任何人都可以帮助我开发任何可以为密钥提供价值的 API。

使用 Spring Boot 可以接受空值的正确杰克逊反序列化配置是什么?目前我正在使用如下 -

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

【问题讨论】:

    标签: gson json-deserialization spring-boot-configuration


    【解决方案1】:

    我已经编写了一些反序列化和序列化您的案例的测试,也许这会对您有所帮助

    GSON 总是反序列化 null 对象,如果你想改变,写你的适配器

    我使用 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;
      }
    }
    

    【讨论】:

    • DEV-Jacol - 如果我们直接采用 inputJson 而不是传递给控制器​​方法,则此方法有效,但是当我将 inputJSON 传递给 Spring 引导控制器时,这对我不起作用,因此我已将方法签名从 Object 更改为字符串然后它起作用了。发布了我所做的解决方案。谢谢
    【解决方案2】:

    我通过将 Spring Boot 端点方法参数签名从 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
    

    }

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-08-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多