GSON反序列化空值不起作用
GSON deserializing null value not working
我正在尝试反序列化 json 字符串。我尝试了不同的 API,但没有找到解决方案。在这里,我试图在 json 以下进行反序列化,并希望读取每个 field/element 的值。下面的示例 -
String inputJson = "{"phone":null, "address":"underworld"}";
LinkedTreeMap map = new Gson().fromJson(inputJson , LinkedTreeMap.class);
当我说 map.containsKey("phone), it is giving as false, it means "phone" 元素不存在于 json 字符串中时。但是,这是不正确的,因为我们可以看到输入 json.
中存在元素
任何人都可以帮助我 API 也可以提供有价值的钥匙。
使用 spring 启动可以接受空值的正确 jackson 反序列化配置是什么?目前我正在使用如下 -
pubic ObjectMapper objectMapper(Jckson3OjectMapperBuilder builder) {
ObjectMapper mapper = builder.build();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper;
}
我已经编写了一些反序列化和序列化您的案例的测试,也许这会对您有所帮助
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;
}
}
我通过将 spring 引导端点方法参数签名从对象更改为字符串解决了这个问题。早些时候它是对象类型,因为它只是忽略字符串中具有空值的键。在控制器中,我正在检查密钥是否存在,如下所示 -
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
}
我正在尝试反序列化 json 字符串。我尝试了不同的 API,但没有找到解决方案。在这里,我试图在 json 以下进行反序列化,并希望读取每个 field/element 的值。下面的示例 -
String inputJson = "{"phone":null, "address":"underworld"}";
LinkedTreeMap map = new Gson().fromJson(inputJson , LinkedTreeMap.class);
当我说 map.containsKey("phone), it is giving as false, it means "phone" 元素不存在于 json 字符串中时。但是,这是不正确的,因为我们可以看到输入 json.
中存在元素任何人都可以帮助我 API 也可以提供有价值的钥匙。
使用 spring 启动可以接受空值的正确 jackson 反序列化配置是什么?目前我正在使用如下 -
pubic ObjectMapper objectMapper(Jckson3OjectMapperBuilder builder) {
ObjectMapper mapper = builder.build();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
return mapper;
}
我已经编写了一些反序列化和序列化您的案例的测试,也许这会对您有所帮助
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;
}
}
我通过将 spring 引导端点方法参数签名从对象更改为字符串解决了这个问题。早些时候它是对象类型,因为它只是忽略字符串中具有空值的键。在控制器中,我正在检查密钥是否存在,如下所示 -
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
}