【发布时间】:2020-09-18 09:47:29
【问题描述】:
我有这门课
Myclass
{
transient String field1;
transient String field2;
... // other non transient fields
}
我存储序列化对象,以这种方式通过网络使用它们(不包括瞬态字段)。
但是,仅针对一种特殊情况,我需要在序列化中包含 field2。
有没有办法在使用 gson 的序列化中不排除某个瞬态字段?
【问题讨论】:
我有这门课
Myclass
{
transient String field1;
transient String field2;
... // other non transient fields
}
我存储序列化对象,以这种方式通过网络使用它们(不包括瞬态字段)。
但是,仅针对一种特殊情况,我需要在序列化中包含 field2。
有没有办法在使用 gson 的序列化中不排除某个瞬态字段?
【问题讨论】:
解决方案 0:
为类使用自定义类型适配器。
考虑
@JsonAdapter(KA.class)
class K{
private transient String name;
private transient String password;
}
class Entity_Adapter extends TypeAdapter<Entity>{
@Override
public void write(JsonWriter out, Entity value) throws IOException {
out.beginObject();
out.name("name");
out.value(value.getName());
out.name("password");
out.value(value.getPassword());
out.endObject();
}
@Override
public Entity read(JsonReader in) throws IOException {
Entity k=new Entity();
in.beginObject();
in.nextName();
k.setName(in.nextString());
in.nextName();
k.setPassword(in.nextString());
in.endObject();
return k;
}
}
完整示例here
解决方案 1: (不可靠)
添加另一个非transient 字段,并始终为其复制field2 的任何新设置值。例如
transient String field1;
transient String field2;
@SerializedName("field2")
private String field2_non_trans;
public void setField2(String arg_val){
this.field2 = arg_val;
this.field2_non_trans = arg_val;
}
public String getField2(){
if(field2 == null){
field2 = field2_non_trans;
}
return field2;
}
完整示例here
但是您必须跟踪 每个更改到 field2,以保持 field2_non_trans 的 val 副本始终更新,所以如果 field2 是由构造函数设置,或超出其setter 函数,您必须确保为field2_non_trans 设置值副本
反序列化也是如此,您必须:
field2_non_trans的反序列化值设置为field2。getField2()方法返回field2_non_trans,其中field2就是null
解决方案 2:
标记field2 非瞬态。
【讨论】:
您可以将您的瞬态字段与另一个非瞬态字段复制并从瞬态字段设置器中写入值。这个想法是每次瞬态字段更新时更新克隆字段。
【讨论】:
尽管由于此类问题我绝不建议对不同的库使用相同的类,但您可以轻松管理 Gson 将 exclusion strategies 应用于被序列化和反序列化的字段的方式。
public final class TransientExclusionStrategy
implements ExclusionStrategy {
private static final ExclusionStrategy instance = new TransientExclusionStrategy();
private TransientExclusionStrategy() {
}
public static ExclusionStrategy getInstance() {
return instance;
}
@Override
public boolean shouldSkipField(final FieldAttributes attributes) {
@Nullable
final Expose expose = attributes.getAnnotation(Expose.class);
if ( expose == null ) {
return attributes.hasModifier(Modifier.TRANSIENT);
}
return !expose.serialize();
}
@Override
public boolean shouldSkipClass(final Class<?> clazz) {
return false;
}
}
此实现将通过以下单元测试:
public final class TransientExclusionStrategyTest {
private static final String EXPOSED = "EXPOSED";
private static final String IGNORED = "IGNORED";
@SuppressWarnings("all")
private static final class MyClass {
final String s0 = EXPOSED; // no explicit expose, serialized by default
@Expose(serialize = false)
final String s1 = IGNORED; // ignored by annotation
@Expose(serialize = true)
final String s2 = EXPOSED; // serialized by annotation
final transient String ts0 = IGNORED; // no explicit expose, ignored by default
@Expose(serialize = false)
final transient String ts1 = IGNORED; // ignored by annotation
@Expose(serialize = true)
final transient String ts2 = EXPOSED; // serialized by annotation
}
@Test
public void test() {
final Gson gson = new GsonBuilder()
.addSerializationExclusionStrategy(TransientExclusionStrategy.getInstance())
.create();
final JsonObject json = (JsonObject) gson.toJsonTree(new MyClass());
for ( final Map.Entry<String, JsonElement> e : json.entrySet() ) {
final String stringValue = e.getValue().getAsString();
Assertions.assertEquals(EXPOSED, stringValue, () -> "Expected " + EXPOSED + " but was " + stringValue + " for " + e.getKey());
}
}
}
因此,您不需要为每个这样的“特殊”类处理任何特殊类型适配器或引入中间字段(不一定与您正在使用的其他库和框架处于冲突状态)。
【讨论】: