鉴于POJO:
class Person {
@Expose
String name;
@Expose
String phone;
@Expose
String fax;
}
我想"电话"在任何时候都要序列化,但"传真"只有当它不为空时。所以给了一个人" John"没有电话或传真:
电流:
{ "name": "John", "phone": null, "fax": null }
我需要什么:
{ "name": "John", "phone": null }
是否有类似的东西:
@Expose (serialize_if_null = false)
瞬态不起作用,因为如果它有值,我仍然希望它被序列化。
使用ExclusionStrategy,我可以定义字段,但我似乎找不到获取值的方法。
由于
答案 0 :(得分:2)
使用ExclusionStrategy,我可以定义字段,但我似乎找不到获取值的方法。
是的,它没有提供确定当前字段值的方法。这是因为Gson ReflectiveTypeAdapterFactory
在内部工作的方式(BoundField.serialized
为final
且仅解析一次):
@Override public boolean writeField(Object value) throws IOException, IllegalAccessException {
if (!serialized) return false;
Object fieldValue = field.get(value);
return fieldValue != value; // avoid recursion for example for Throwable.cause
}
for (BoundField boundField : boundFields.values()) {
if (boundField.writeField(value)) {
out.name(boundField.name);
boundField.write(out, value);
}
}
这种行为无法改变,但我认为分离应用程序对象及其序列化表示(参见Data Transfer Object模式)是一个很好的设计选择,以便不混合概念并使应用程序组件松散耦合(从Gson有一天只会修改相应的DTO课程。
如果您可以将DTO引入您的应用程序,那么您可以为这两种方案创建单独的DTO类:保留phone
并根据fax
字段值丢弃fax
class PersonDto {
@Expose String name;
@Expose String phone;
PersonDto(final Person person) {
name = person.name;
phone = person.phone;
}
}
class PersonDtoWithFax extends PersonDto {
@Expose String fax;
PersonDtoWithFax(final Person person) {
super(person);
fax = person.fax;
}
}
在这种情况下,序列化是直截了当的:
final Gson gson = new GsonBuilder()
.serializeNulls()
.create();
final Person person = new Person();
person.name = "John";
final PersonDto personDto = person.fax == null
? new PersonDto(person)
: new PersonDtoWithFax(person);
System.out.println(gson.toJson(personDto));
如果你不想本身介绍隔离的DTO概念,你可能想要实现一个在实现上稍微复杂一点的自定义序列化程序,并且可能由于属性名称硬编码而容易出错(但你可以当然是好的测试,或从java.lang.reflect.Field
个实例中提取名称。
final class SpecialJsonSerializer<T>
implements JsonSerializer<T> {
private final Gson gson; // Unfortunately, Gson does not provide much from JsonSerialiationContext, so we have to get it ourselves
private final Iterable<String> excludeIfNull;
private SpecialJsonSerializer(final Gson gson, final Iterable<String> excludeIfNull) {
this.gson = gson;
this.excludeIfNull = excludeIfNull;
}
static <T> JsonSerializer<T> getSpecialJsonSerializer(final Gson gson, final Iterable<String> excludeIfNull) {
return new SpecialJsonSerializer<>(gson, excludeIfNull);
}
@Override
public JsonElement serialize(final T object, final Type type, final JsonSerializationContext context) {
// context.serialize(person, type) cannot work due to infinite recursive serialization
// therefore the backing Gson instance is used
final JsonObject jsonObject = gson.toJsonTree(object, type).getAsJsonObject();
for ( final String propertyName : excludeIfNull ) {
final JsonElement property = jsonObject.get(propertyName);
if ( property != null && property.isJsonNull() ) {
jsonObject.remove(propertyName);
}
}
return jsonObject;
}
}
我不太确定,但我认为从内存消耗的角度来看,创建用于序列化而不是使用DTO的JSON树可能会稍微昂贵一些(至少因为更复杂的JsonElement
结构)
// Both Gson instances must have serializeNulls()
final Gson gson = new GsonBuilder()
.serializeNulls()
.create();
final Gson gsonWrapper = new GsonBuilder()
.serializeNulls()
.registerTypeAdapter(Person.class, getSpecialJsonSerializer(gson, singletonList("fax")))
.create();
final Person person = new Person();
person.name = "John";
System.out.println(gsonWrapper.toJson(person));
两种解决方案输出:
{ “名称”: “约翰”, “电话”:空}