如何让Gson反序列化计算值?
这是一个过于简单的例子:
@SerializedName("members")
@Expose
private final List<String> members;
@SerializedName("size")
@Expose(serialize = true, deserialize = false)
private final int size;
public Club(List<String> members) {
this.members = members;
this.size = members.size();
}
size
为deserialize
时,我false
= 0;
size
为deserialize
时,我会true
= old_size 。
有没有办法强迫&#34;强迫&#34;重新计算?
我知道我可以在getter中执行此操作
答案 0 :(得分:1)
这是因为Gson在反序列化期间不调用构造函数。 但是,可以拦截实例化后的对象阶段,并对最近反序列化的对象进行一些额外的操作。
以下是一个示例@PostConstruct
- 知晓型适配器工厂:
final class PostConstructTypeAdapterFactory
implements TypeAdapterFactory {
// No intermediate state, can be a singleton
private static final TypeAdapterFactory postConstructTypeAdapterFactory = new PostConstructTypeAdapterFactory();
private PostConstructTypeAdapterFactory() {
}
// However, making the constructor private encapsulates the way it's instantiated
static TypeAdapterFactory getPostConstructTypeAdapterFactory() {
return postConstructTypeAdapterFactory;
}
@Override
public <T> TypeAdapter<T> create(final Gson gson, final TypeToken<T> typeToken) {
final List<Method> postConstructMethods = getPostConstructMethods(typeToken.getRawType());
if ( postConstructMethods.isEmpty() ) {
// If no post-construct methods found, just let Gson to pick up the next best-match type adapter itself
return null;
}
// Obtain the "original" type adapter
final TypeAdapter<T> delegateTypeAdapter = gson.getDelegateAdapter(this, typeToken);
return new PostConstructTypeAdapter<>(delegateTypeAdapter, postConstructMethods);
}
private static List<Method> getPostConstructMethods(final Class<?> clazz) {
if ( clazz.isPrimitive() ) {
// Nothing to do with primitives
return emptyList();
}
// The following stream operation collects all `@PostConstruct` methods
// from java.lang.Object to the concrete class
// where all @PostConstruct methods must satisfy the following conditions (differ from the original `@PostConstruct` contract):
// * have no paramaters
// * return nothing (but it looks like a too strict limitation, though)
// * be annotated with `@PostConstruct`
return superToSub(clazz)
.stream()
.flatMap(c -> Stream.of(c.getDeclaredMethods()))
.filter(m -> {
final int parameterCount = m.getParameterCount();
if ( parameterCount != 0 ) {
return false;
}
final Class<?> returnType = m.getReturnType();
if ( returnType != void.class ) {
return false;
}
return m.isAnnotationPresent(PostConstruct.class);
})
.peek(m -> m.setAccessible(true))
.collect(toList());
}
private static List<Class<?>> superToSub(final Class<?> clazz) {
final List<Class<?>> hierarchy = subToSuper(clazz);
Collections.reverse(hierarchy);
return hierarchy;
}
private static List<Class<?>> subToSuper(final Class<?> clazz) {
final List<Class<?>> hierarchy = new ArrayList<>();
for ( Class<?> c = clazz; c != null; c = c.getSuperclass() ) {
hierarchy.add(c);
}
return hierarchy;
}
private static final class PostConstructTypeAdapter<T>
extends TypeAdapter<T> {
private final TypeAdapter<T> delegateTypeAdapter;
private final Iterable<Method> postConstructMethods;
private PostConstructTypeAdapter(final TypeAdapter<T> delegateTypeAdapter, final Iterable<Method> postConstructMethods) {
this.delegateTypeAdapter = delegateTypeAdapter;
this.postConstructMethods = postConstructMethods;
}
@Override
public void write(final JsonWriter out, final T value)
throws IOException {
// Nothing special to do on write, so just delegate the job
delegateTypeAdapter.write(out, value);
}
@Override
public T read(final JsonReader in)
throws IOException {
try {
// Whilst on read there's a need to apply all post-construction methods
final T read = delegateTypeAdapter.read(in);
for ( final Method method : postConstructMethods ) {
method.invoke(read);
}
return read;
} catch ( IllegalAccessException | InvocationTargetException ex ) {
throw new IOException(ex);
}
}
}
}
然后,您的Club
可以包含一个后构造函数:
@PostConstruct
private void postConstruct()
throws NoSuchFieldException, IllegalAccessException {
final Field sizeField = Club.class.getDeclaredField("size");
sizeField.setAccessible(true);
sizeField.set(this, members.size());
}
使用ad-hoc测试的示例:
private static final Gson gson = new GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.registerTypeAdapterFactory(getPostConstructTypeAdapterFactory())
.create();
public static void main(final String... args) {
final List<String> members = ImmutableList.of("Foo", "Bar", "Baz");
final Club beforeClub = new Club(members);
final List<String> beforeMembers = beforeClub.members;
final int beforeSize = beforeClub.size;
final String clubJson = gson.toJson(beforeClub);
System.out.println(clubJson);
final Club afterClub = gson.fromJson(clubJson, Club.class);
final List<String> afterMembers = afterClub.members;
final int afterSize = afterClub.size;
if ( !beforeMembers.equals(afterMembers) ) {
throw new AssertionError("`members` values do not match");
}
if ( beforeSize != afterSize ) {
throw new AssertionError("`size` values do not match");
}
System.out.println("SUCCESS");
}
输出:
{&#34;会员&#34;:[&#34;富&#34;&#34;酒吧&#34;&#34;巴兹&#34],&#34;大小&#34; :3}
成功
查看更多:
@PostConstruct
support在官方Gson存储库中。@PostConstruct
Javadoc。