将特定的瞬态字段包含在使用GSON进行JSON序列化时。

huangapple go评论79阅读模式
英文:

Include a certain transient field in JSON serialization with GSON

问题

我有这个类

Myclass
{
  transient String field1;
  transient String field2;
  ... // 其他非 transient 字段
}

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

然而,只有在一个特定的情况下,我需要在序列化中包括 field2。

是否有一种方法可以使用 gson 在序列化中不排除特定的 transient 字段呢?

英文:

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.

Is there a way to not to exclude a certain transient field in serialization using gson?

答案1

得分: 1

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

完整示例可见这里

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

完整示例可见这里

但是你必须追踪对field2的每次更改,以便始终保持field2_non_trans的复制值更新,因此如果field2是由构造函数设置的,或者在其setter函数之外设置的,你必须确保为field2_non_trans设置值的复制。

反序列化也是同样的道理,你必须:

  • 一旦反序列化完成,你需要使用一个方法将field2_non_trans的反序列化值设置给field2
  • 或者只需通过getField2()方法返回field2_non_trans,其中field2null

Solution 2:

field2标记为非transient

英文:

Solution 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&lt;Entity&gt;{
    @Override
    public void write(JsonWriter out, Entity value) throws IOException {
        out.beginObject();
        
        out.name(&quot;name&quot;);
        out.value(value.getName());
        
        out.name(&quot;password&quot;);
        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 example here

Solution 1: (not robust)

Add another non-transient field, and always copy any new set value for field2 for it too. e.g.

transient String field1;
transient String field2;

@SerializedName(&quot;field2&quot;)
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 sample here

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

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.
  • Or simply return field2_non_trans by getField2() method, where field2 is null

Solution 2:

Mark that field2 non-transient.

答案2

得分: 0

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

英文:

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.

答案3

得分: 0

尽管我永远不会建议在不同的库中使用相同的类,因为会出现像这样的问题,但你可以轻松地管理 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&lt;?&gt; 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());
		}
	}

}

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

英文:

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.

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&lt;?&gt; clazz) {
		return false;
	}

}

This implementation would pass the following unit test:

public final class TransientExclusionStrategyTest {

	private static final String EXPOSED = &quot;EXPOSED&quot;;
	private static final String IGNORED = &quot;IGNORED&quot;;

	@SuppressWarnings(&quot;all&quot;)
	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&lt;String, JsonElement&gt; e : json.entrySet() ) {
			final String stringValue = e.getValue().getAsString();
			Assertions.assertEquals(EXPOSED, stringValue, () -&gt; &quot;Expected &quot; + EXPOSED + &quot; but was &quot; + stringValue + &quot; for &quot; + 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).

huangapple
  • 本文由 发表于 2020年9月18日 17:47:29
  • 转载请务必保留本文链接:https://go.coder-hub.com/63953357.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定