简体   繁体   English

使用 GSON 在 JSON 序列化中包含某个瞬态字段

[英]Include a certain transient field in JSON serialization with GSON

I have this class我有这堂课

Myclass
{
  transient String field1;
  transient String field2;
  ... // other non transient fields
}

I store serialized objects, use them through network this way (transient fields excluded).我存储序列化对象,以这种方式通过网络使用它们(不包括瞬态字段)。

However, just for one particular case, I need to include field2 in serialization.但是,仅对于一种特殊情况,我需要在序列化中包含 field2。

Is there a way to not to exclude a certain transient field in serialization using gson?有没有办法不使用 gson 在序列化中排除某个瞬态字段?

Solution 0:解决方案0:

Use a custom type adapter for the class.为类使用自定义类型适配器。

Considering考虑

@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;
    }
}

full examplehere完整的例子在这里

Solution 1: (not robust)解决方案1:(不健壮)

Add another non- transient field, and always copy any new set value for field2 for it too.添加另一个非transient字段,并始终为其复制field2任何新设置值。 eg例如

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;
}

full samplehere完整样本在这里

But you MUST track every change to that field2 , to keep the copy of the val for field2_non_trans updated always, so if that field2 is set by constructor, or out-of its setter function, you have to be sure you set the value copy for field2_non_trans但是您必须跟踪对该field2每次更改,以始终更新field2_non_trans的 val 副本,因此如果该field2是由构造函数设置的,或者在其setter函数之外,您必须确保为field2_non_trans

Same for deserializing, you have to either:对于反序列化,您必须:

  • once deserializing is over, you need to set the deserialized value of field2_non_trans to field2 using a method.一旦反序列化结束,您需要使用方法将field2_non_trans的反序列化值设置为field2
  • Or simply return field2_non_trans by getField2() method, where field2 is null或者简单地通过getField2()方法返回field2_non_trans ,其中field2null

Solution 2:解决方案2:

Mark that field2 non-transient.将该field2标记为非瞬态。

You can duplicate your transient field with another non transient and write value from transient field setter.您可以使用另一个非瞬态字段复制瞬态字段,并从瞬态字段设置器中写入值。 The idea is update clone field every time transient field updates.这个想法是每次瞬态字段更新时更新克隆字段。

Despite I would never recommend using the same class for different libraries due to issues like this, you can easily manage the way Gson applies exclusion strategies to fields being serialized and deserialized.尽管由于此类问题,我永远不会建议对不同的库使用相同的类,但您可以轻松管理 Gson 将排除策略应用于正在序列化和反序列化的字段的方式。

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;
    }

}

This implementation would pass the following unit test:此实现将通过以下单元测试:

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());
        }
    }

}

Thus you don't need to handle any special type adapters for every such a "special" class or introduce intermediate fields (that's not necessarily not in conflict state with other libraries and frameworks you're using).因此,您不需要为每个这样的“特殊”类处理任何特殊类型的适配器或引入中间字段(这不一定与您正在使用的其他库和框架处于冲突状态)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM