public abstract class MaskedTypeAdapterFactory
implements TypeAdapterFactory {
private final int exposeFront;
private final int exposeRear;
private final char mask;
private MaskedTypeAdapterFactory(final int exposeFront, final int exposeRear, final char mask) {
this.exposeFront = exposeFront;
this.exposeRear = exposeRear;
this.mask = mask;
}
// must be "baked" into the class (name only represents the configuration)
public static final class _2_2_asterisk
extends MaskedTypeAdapterFactory {
private _2_2_asterisk() {
super(2, 2, '*');
}
}
@Override
@Nullable
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
if ( typeToken.getRawType() != String.class ) {
return null;
}
@SuppressWarnings("unchecked")
final TypeAdapter<String> delegate = (TypeAdapter<String>) gson.getAdapter(typeToken);
final TypeAdapter<String> typeAdapter = new TypeAdapter<String>() {
@Override
public void write(final JsonWriter out, final String value)
throws IOException {
// mask the value
final int length = value.length();
final char[] buffer = value.toCharArray();
for ( int i = exposeFront; i < length - exposeRear; i++ ) {
buffer[i] = mask;
}
out.value(new String(buffer));
}
@Override
public String read(final JsonReader in)
throws IOException {
return delegate.read(in);
}
}
.nullSafe();
@SuppressWarnings("unchecked")
final TypeAdapter<T> adapter = (TypeAdapter<T>) typeAdapter;
return adapter;
}
}
@NoArgsConstructor
@AllArgsConstructor
final class MyClass {
@SerializedName("qwerty")
@Mask(exposeFront = 2, exposeRear = 2, mask = "*")
// unfortunately, this must duplicate the @Mask annotation values
// since type adapter (factories) do not accept supplemental information
// and Java annotations can only accept compile-time constants
@JsonAdapter(MaskedTypeAdapterFactory._2_2_asterisk.class)
@SuppressWarnings("unused")
private String qwerty;
}
测试:
public final class MaskedTypeAdapterFactoryTest {
private static final Gson gson = new GsonBuilder()
.disableHtmlEscaping()
.disableInnerClassSerialization()
.create();
@Test
public void test() {
final String actual = gson.toJson(new MyClass("1234567890"));
final String expected = "{\"qwerty\":\"12******90\"}";
Assertions.assertEquals(expected, actual);
}
}
1条答案
按热度按时间unftdfkk1#
格森
ReflectiveTypeAdapterFactory
,负责“普通”对象序列化和反序列化,不可能增强以支持任何其他注解,如@Masked
. 它只能使用如下注解@Expose
(间接通过排除策略),@SerializedName
还有一些人喜欢@Since
以及@Until
(也是排除策略)。注意:这些注解是文档化的,默认情况下是受支持的。一般来说,gson建议对声明类使用类型适配器,MyClass
,但这也意味着您必须管理所有字段,并确保在更改类后更新相应的类型适配器。更糟糕的是,添加自定义类型适配器会使这些注解失去支持。另一种解决方法是注入一个特殊的字符串类型的适配器工厂,它可以完成这个任务,但是由于注入机制的原因,这既有局限性,又需要复制
@Masked
注解值(如果您在代码的其他地方使用注解)和中的类型适配器工厂配置@JsonAdapter
.测试:
这可能是gson中最强大的方法。