Java Gson序列化:如果为null,则排除某些字段,但不是全部字段

Java Gson序列化:如果为null,则排除某些字段,但不是全部字段,java,gson,Java,Gson,给定一个POJO: class Person { @Expose String name; @Expose String phone; @Expose String fax; } 我希望“phone”在任何时候都被序列化,但“fax”只有在它不为null时才被序列化。所以给一个没有电话或传真的人“John”: 当前: { "name": "John", "phone": null, "fax": null } 我需要的是: { "name":

给定一个POJO:

class Person {
    @Expose
    String name;
    @Expose
    String phone;
    @Expose
    String fax;
}
我希望“phone”在任何时候都被序列化,但“fax”只有在它不为null时才被序列化。所以给一个没有电话或传真的人“John”:

当前:

{ "name": "John", "phone": null, "fax": null }
我需要的是:

{ "name": "John", "phone": null }
是否有类似于:

@Expose (serialize_if_null = false)
transient不起作用,因为如果它有值,我仍然希望它序列化

使用排除策略,我可以定义字段,但似乎找不到获取值的方法

谢谢

使用排除策略,我可以定义字段,但似乎找不到获取值的方法

是的,它不提供确定当前字段值的方法。这是因为Gson
ReflectTypeAdapterFactory
在内部是如何工作的(序列化的
BoundField.serialized
final
并且只解析一次):

@Override public boolean writeField(对象值)引发IOException,IllegalAccessException{
如果(!serialized)返回false;
对象fieldValue=field.get(值);
return fieldValue!=value;//避免递归,例如Throwable.cause
}
for(BoundField-BoundField:boundFields.values()){
if(boundField.writeField(值)){
out.name(boundField.name);
边界字段。写入(输出,值);
}
}
这种行为是无法改变的,但我认为分离应用程序对象及其序列化表示(请参见模式)是一种很好的设计选择,这样可以避免混淆概念,并使应用程序组件松散耦合(将来从Gson迁移只需对相应的DTO类进行修改)

如果您对在应用程序中引入DTO还满意,那么您可以为这两种情况创建单独的DTO类:根据
fax
字段值,保留
phone
和丢弃
fax

class PersonDto{
@公开字符串名称;
@暴露字符串电话;
PersonDto(最终用户){
name=person.name;
phone=person.phone;
}
}
class PersonDtoWithFax扩展PersonDto{
@公开字符串传真;
人员传真(最终人员){
超级(人);
传真=person.fax;
}
}
在这种情况下,序列化是直接进行的:

final Gson Gson=new GsonBuilder()
.nulls()
.create();
最终人员=新人员();
person.name=“约翰”;
final PersonDto PersonDto=person.fax==null
? 新人
:新员工传真(人);
System.out.println(gson.toJson(personDto));
如果您不想引入隔离DTO概念本身,那么可能需要实现一个自定义序列化程序,该序列化程序的实现比较复杂,并且可能由于属性名硬编码而容易出错(当然,您可以进行良好的测试,或者从
java.lang.reflect.Field
实例中提取名称)

final class SpecialJsonSerializer
实现JsonSerializer{
private final Gson Gson;//不幸的是,Gson没有从JsonSerialiationContext提供太多,所以我们必须自己获得它
私有最终可限定排除项为null;
专用专用JSONSerializer(最终Gson Gson,最终Iterable excludeIfNull){
this.gson=gson;
this.excludeIfNull=excludeIfNull;
}
静态JsonSerializer getSpecialJsonSerializer(最终Gson Gson,最终Iterable excludeIfNull){
返回新的SpecialJsonSerializer(gson,excludeIfNull);
}
@凌驾
公共JsonElement序列化(最终T对象、最终类型、最终JsonSerializationContext){
//由于无限递归序列化,serialize(person,type)无法工作
//因此,使用了支持Gson实例
final-JsonObject-JsonObject=gson.toJsonTree(对象,类型).getAsJsonObject();
for(最终字符串propertyName:excludeIfNull){
最终的JsonElement属性=jsonObject.get(propertyName);
if(property!=null&&property.isJsonNull(){
jsonObject.remove(propertyName);
}
}
返回jsonObject;
}
}
我不是很确定,但我认为从内存消耗的角度来看,为序列化目的创建JSON树而不是使用DTO可能会稍微贵一点(至少是因为更复杂的
JsonElement
结构)

//两个Gson实例都必须具有serializeNulls()
最终Gson Gson=新的GsonBuilder()
.nulls()
.create();
最终Gson-gsonWrapper=new-GsonBuilder()
.nulls()
.registerTypeAdapter(Person.class,getSpecialJsonSerializer(gson,singletonList(“传真”))
.create();
最终人员=新人员();
person.name=“约翰”;
System.out.println(gsonWrapper.toJson(person));
两种解决方案的输出:

{“name”:“John”,“phone”:null}


“Person”对象只是一个简化的示例,因此DTO样式的解决方案可能不是最干净的解决方案……可能类似于personbuilder().withfax().build()有些东西???@sikidhart嗯,这要看情况而定,我会使用DTO来降低出错率。注意技巧:当调用
toJson
方法时,Gson可以根据对象的实际类型来区分
PersonDto
PersonDtoWithFax
。您提到的生成器模式只是创建DTO的另一种方式(我使用构造函数只是为了简单起见)。如果你想使用builder,一切都取决于你,你可以使用
with fax(…)
返回PersonDtoWithFaxBuilder`实例轻松创建自己的生成器。但是引入构造函数通常会带来一些复杂性。你自己决定。