Java Gson在反序列化对象时忽略null
我想反序列化一个json字符串,该字符串在Java中包含空值。我想将对象反序列化为Java Gson在反序列化对象时忽略null,java,json,gson,deserialization,Java,Json,Gson,Deserialization,我想反序列化一个json字符串,该字符串在Java中包含空值。我想将对象反序列化为Properties对象。json字符串类似于: {"prop1":null, "propr2":"fancy value"} 当我使用 String json = // new Gson().fromJson(json, Properties.class); 我得到一个空指针异常,因为Hastable进入Properties对象。如何指示Gson忽略空值的反序列化?请参阅: Gson Gson=new Gso
Properties
对象。json字符串类似于:
{"prop1":null, "propr2":"fancy value"}
当我使用
String json = //
new Gson().fromJson(json, Properties.class);
我得到一个空指针异常,因为Hastable
进入Properties
对象。如何指示Gson忽略空值的反序列化?请参阅:
Gson Gson=new GsonBuilder().serializeNulls().create()代码>我们有以下解决方案:
1。所有数据类都需要扩展抽象类
abstract class PoJoClass
2。创建此安全反序列化程序以从JSON中删除空值
class SafeDeserializer<T : PoJoClass>(private val gson: Gson) :JsonDeserializer<T> {
override fun deserialize(json: JsonElement?, typeOfT: Type?, context: JsonDeserializationContext?): T {
val jsonObject = json as JsonObject
removeNullsFromJson(jsonObject)
return gson.fromJson(jsonObject, typeOfT)
}
private fun removeNullsFromJson(jsonObject: JsonObject) {
val iterator = jsonObject.keySet().iterator()
while (iterator.hasNext()) {
val key = iterator.next()
when(val json = jsonObject[key]){
is JsonObject -> removeNullsFromJson(json)
is JsonNull -> iterator.remove()
}
}
}
}
类安全反序列化器(private val gson:gson):JsonDeserializer{
重写有趣的反序列化(json:JsonElement?、typeOfT:Type?、context:JsonDeserializationContext?):T{
val jsonObject=json作为jsonObject
removeNullsFromJson(jsonObject)
返回gson.fromJson(jsonObject,typeOfT)
}
private fun removeNullsFromJson(jsonObject:jsonObject){
val iterator=jsonObject.keySet().iterator()
while(iterator.hasNext()){
val key=iterator.next()
当(val json=jsonObject[key]){
是JsonObject->removeNullsFromJson(json)
是JsonNull->iterator.remove()
}
}
}
}
3。并在您的GSON实例中注册它
val gson = Gson().newBuilder()
.registerTypeHierarchyAdapter(PoJoClass::class.java, SafeDeserializer<PoJoClass>(Gson()))
.create()
val gson=gson().newBuilder()
.registerTypeHierarchyAdapter(PoJoClass::class.java,安全反序列化器(Gson())
.create()
问题确实在于Gson的默认适配器试图将null
放入属性中,这是被禁止的
要解决这个问题,您可以为属性
编写自己的。然后,您必须使用GsonBuilder
创建Gson实例,在该实例上输入适配器
下面显示了这样一个适配器的外观。更严格的是,它在序列化过程中防止非字符串键和值(Gson的默认适配器不这样做),因为它们在反序列化过程中会导致问题。但是,您可以使用替换它并将序列化委托给Gson的适配器
private static final TypeAdapter PROPERTIES\u ADAPTER=new TypeAdapter(){
@凌驾
公共属性读取(JsonReader in)引发IOException{
in.beginObject();
属性=新属性();
while(在.hasNext()中){
String name=in.nextName();
JsonToken peek=in.peek();
//忽略空值
if(peek==JsonToken.NULL){
in.nextNull();
继续;
}
//允许Json布尔值
else if(peek==JsonToken.BOOLEAN){
setProperty(名称,Boolean.toString(in.nextBoolean());
}
//应为字符串或数字
否则{
setProperty(名称,在.nextString()中);
}
}
in.endObject();
归还财产;
}
私有字符串关联(对象obj){
if(obj.getClass()!=String.class){
抛出新的IllegalArgumentException(“属性包含非字符串对象”+obj);
}
返回(字符串)对象;
}
/*
*还可以委托给Gson的实现进行序列化。
*但是,如果属性包含非字符串值,则不会失败,
*这将导致再次反序列化Json时出现问题。
*/
@凌驾
public void write(JsonWriter out,Properties)抛出IOException{
out.beginObject();
对于(Map.Entry:properties.entrySet()){
//确保键是字符串,否则为属性
//无法再次反序列化
out.name(asString(entry.getKey());
对象值=entry.getValue();
//要宽容,允许数字和布尔值作为值
if(值instanceof Number){
输出值((数字)值);
}else if(布尔值实例){
out.value((布尔)值);
}否则{
//要求该值为字符串
out.value(asString(value));
}
}
out.endObject();
}
}.nullSafe();//处理null属性,例如`Properties props=null`
公共静态void main(字符串[]args)引发IOException{
Gson Gson=new GsonBuilder()
//注册自定义类型适配器
.registerTypeAdapter(Properties.class,Properties\u适配器)
.create();
字符串json=“{\'prop1\':true,\'prop2\':\'text\',\'prop3\':null}”;
属性反序列化=gson.fromJson(json,Properties.class);
System.out.println(“反序列化:+反序列化”);
属性=新属性();
属性。设置属性(“属性”、“文本”);
//不鼓励放置非字符串,但类型适配器支持这些
properties.put(“布尔”,true);
物业。出售(“编号”,1234);
System.out.println(“序列化:+gson.toJson(属性));
}
这是用于序列化的。我需要反序列化我看到它是用于序列化的,但我想反序列化也会起作用。假设gson产生了一些可以再次反序列化的东西,我也遇到了同样的问题,即使是使用自定义反序列化程序。我可以检查空值,但这需要很多检查。api中有什么可以用来检查属性是否存在以及值是否为非空值的内容吗?有时我认为提供属性扩展会更简单。@mat_boy您介意将标题更改为“在反序列化属性对象时Gson忽略空值”之类的内容吗?因为这似乎就是你的问题所在。忽略null
值通常是可能的
private static final TypeAdapter<Properties> PROPERTIES_ADAPTER = new TypeAdapter<Properties>() {
@Override
public Properties read(JsonReader in) throws IOException {
in.beginObject();
Properties properties = new Properties();
while (in.hasNext()) {
String name = in.nextName();
JsonToken peeked = in.peek();
// Ignore null values
if (peeked == JsonToken.NULL) {
in.nextNull();
continue;
}
// Allow Json boolean
else if (peeked == JsonToken.BOOLEAN) {
properties.setProperty(name, Boolean.toString(in.nextBoolean()));
}
// Expect string or number
else {
properties.setProperty(name, in.nextString());
}
}
in.endObject();
return properties;
}
private String asString(Object obj) {
if (obj.getClass() != String.class) {
throw new IllegalArgumentException("Properties contains non-String object " + obj);
}
return (String) obj;
}
/*
* Could also delegate to Gson's implementation for serialization.
* However, that would not fail if the Properties contains non-String values,
* which would then cause issues when deserializing the Json again.
*/
@Override
public void write(JsonWriter out, Properties properties) throws IOException {
out.beginObject();
for (Map.Entry<Object, Object> entry : properties.entrySet()) {
// Make sure that key is a String, otherwise properties
// cannot be deserialized again
out.name(asString(entry.getKey()));
Object value = entry.getValue();
// Be lenient and allow Numbers and Booleans as values
if (value instanceof Number) {
out.value((Number) value);
} else if (value instanceof Boolean) {
out.value((Boolean) value);
} else {
// Require that value is a String
out.value(asString(value));
}
}
out.endObject();
}
}.nullSafe(); // Handle null Properties, e.g. `Properties props = null`
public static void main(String[] args) throws IOException {
Gson gson = new GsonBuilder()
// Register the custom type adapter
.registerTypeAdapter(Properties.class, PROPERTIES_ADAPTER)
.create();
String json = "{\"prop1\":true, \"prop2\":\"text\", \"prop3\":null}";
Properties deserialized = gson.fromJson(json, Properties.class);
System.out.println("Deserialized: " + deserialized);
Properties properties = new Properties();
properties.setProperty("prop", "text");
// Discouraged to put non-Strings, but type adapter supports these
properties.put("boolean", true);
properties.put("number", 1234);
System.out.println("Serialized: " + gson.toJson(properties));
}