【问题标题】:Include a certain transient field in JSON serialization with GSON在使用 GSON 的 JSON 序列化中包含某个瞬态字段
【发布时间】:2020-09-18 09:47:29
【问题描述】:

我有这门课

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

我存储序列化对象,以这种方式通过网络使用它们(不包括瞬态字段)。

但是,仅针对一种特殊情况,我需要在序列化中包含 field2。

有没有办法在使用 gson 的序列化中不排除某个瞬态字段?

【问题讨论】:

    标签: java android json gson


    【解决方案1】:

    解决方案 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 非瞬态。

    【讨论】:

    • 解决方案 1 和 2 将为其他序列化生成一个 'field2' 字段,这在我的情况下是不行的。但类型适配器,溶胶。 0,看起来很有希望,有机会我会研究一下。谢谢。
    • 编写自定义适配器在技术上解决了这个问题,但是当类很大并且以易于维护的方式编写它是一项挑战时,这是一项繁琐的工作。为这个想法 +1。
    【解决方案2】:

    您可以将您的瞬态字段与另一个非瞬态字段复制并从瞬态字段设置器中写入值。这个想法是每次瞬态字段更新时更新克隆字段。

    【讨论】:

    • 是的,但它不会为其他序列化生成“field2”字段吗?就我而言,这不好。我认为我需要 smt 来针对这种特殊情况使用自定义构建器
    【解决方案3】:

    尽管由于此类问题我绝不建议对不同的库使用相同的类,但您可以轻松管理 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());
            }
        }
    
    }
    

    因此,您不需要为每个这样的“特殊”类处理任何特殊类型适配器或引入中间字段(不一定与您正在使用的其他库和框架处于冲突状态)。

    【讨论】:

      猜你喜欢
      • 2022-01-15
      • 1970-01-01
      • 2016-03-08
      • 2020-05-05
      • 2015-10-18
      • 1970-01-01
      • 1970-01-01
      • 2016-09-27
      • 1970-01-01
      相关资源
      最近更新 更多