我怀疑任何库都支持这种开箱即用的功能,因为它们唯一的职责是处理序列化/反序列化。
因此,为了使 任何 序列化程序按您的意愿工作,最好创建一个对象模拟,然后尝试为特定库调整模拟策略。
首先,让我们为类型创建一个简单的实用程序类:
final class Types {
private Types() {
}
static <T> Class<T> typeToClass(final Type type) {
final Class<?> clazz;
if ( type instanceof Class ) {
clazz = (Class<?>) type;
} else if ( type instanceof ParameterizedType ) {
final ParameterizedType parameterizedType = (ParameterizedType) type;
clazz = typeToClass(parameterizedType.getRawType());
} else {
throw new AssertionError(type);
}
@SuppressWarnings("unchecked")
final Class<T> castClass = (Class<T>) clazz;
return castClass;
}
}
上面的方法只负责从一个类型中解析一个类(至少它试图这样做)。
类是类型,但类型不一定是类。
嗯,这就是他们的 Java 类型系统。
下一个类负责按类型模拟对象。
这有点困难,但 cmets 会有所启发(ImmutableMap 和 ImmutableList 来自 Google Guava)。
final class Mock {
// Cache immutable primitives and wrappers. It's safe
private static final Optional<Byte> defaultByte = Optional.of((byte) 0);
private static final Optional<Short> defaultShort = Optional.of((short) 0);
private static final Optional<Integer> defaultInteger = Optional.of(0);
private static final Optional<Long> defaultLong = Optional.of(0L);
private static final Optional<Float> defaultFloat = Optional.of(0F);
private static final Optional<Double> defaultDouble = Optional.of(0D);
private static final Optional<Character> defaultCharacter = Optional.of('\u0000');
private static final Optional<Boolean> defaultBoolean = Optional.of(false);
private static final Optional<String> defaultString = Optional.of("");
// This is a simple map that can return a value by a known type
private static final Map<Class<?>, Optional<?>> defaultObjectsByIndex = ImmutableMap.<Class<?>, Optional<?>>builder()
.put(byte.class, defaultByte).put(Byte.class, defaultByte)
.put(short.class, defaultShort).put(Short.class, defaultShort)
.put(int.class, defaultInteger).put(Integer.class, defaultInteger)
.put(long.class, defaultLong).put(Long.class, defaultLong)
.put(float.class, defaultFloat).put(Float.class, defaultFloat)
.put(double.class, defaultDouble).put(Double.class, defaultDouble)
.put(char.class, defaultCharacter).put(Character.class, defaultCharacter)
.put(boolean.class, defaultBoolean).put(Boolean.class, defaultBoolean)
.put(String.class, defaultString)
.build();
// Unlike the previous map, searching for an object takes a linear type
// The first-best candidate will be used
// The ifAssignable method is declared below
private static final Collection<? extends Function<? super Type, Optional<?>>> defaultObjectsByFirstBest = ImmutableList.<Function<? super Type, Optional<?>>>builder()
.add(ifAssignable(LinkedList.class, LinkedList::new))
.add(ifAssignable(ArrayList.class, ArrayList::new))
.add(ifAssignable(List.class, ArrayList::new))
.add(ifAssignable(TreeSet.class, TreeSet::new))
.add(ifAssignable(LinkedHashSet.class, LinkedHashSet::new))
.add(ifAssignable(HashSet.class, HashSet::new))
.add(ifAssignable(Set.class, HashSet::new))
.add(ifAssignable(TreeMap.class, TreeMap::new))
.add(ifAssignable(LinkedHashMap.class, LinkedHashMap::new))
.add(ifAssignable(HashMap.class, HashMap::new))
.add(ifAssignable(Map.class, HashMap::new))
.build();
private Mock() {
}
static <T> T create(
final Type type,
final Predicate<? super Type> isTypeSupported,
final Predicate<? super Field> isFieldSupported,
final Function<? super Class<T>, ? extends T> objectCreator,
final BiFunction<Object, Type, Object> postProcess
)
throws Exception {
return create(type, Mock::supplyDefaultInstance, isTypeSupported, isFieldSupported, objectCreator, postProcess);
}
static <T> T create(
final Type type, // Used to instantiate an object
final Function<? super Type, Optional<?>> defaultInstanceSupplier, // Used in order to supply a default object by type
final Predicate<? super Type> isTypeSupported, // Not all types can be serialized
final Predicate<? super Field> isFieldSupported, // Not all fields can be serialized
final Function<? super Class<T>, ? extends T> objectCreator, // If no any default object can be supplied, try to ask for it from elsewhere
final BiFunction<Object, Type, Object> postProcess // This is what is what used to post-process the result object
)
throws Exception {
// Not something we want to support?
if ( !isTypeSupported.test(type) ) {
return null;
}
// Check if we can provide a default value
final Optional<?> maybeT = defaultInstanceSupplier.apply(type);
if ( maybeT.isPresent() ) {
@SuppressWarnings("unchecked")
final T castT = (T) postProcess.apply(maybeT.get(), type);
return castT;
}
final Class<T> clazz = Types.typeToClass(type);
// No? Then let's try instantiate it
final T newT = objectCreator.apply(clazz);
// And iterate it from bottom to top to super classes (java.lang.Object does not have fields)
for ( Class<?> i = clazz; i != null && i != Object.class; i = i.getSuperclass() ) {
for ( final Field field : i.getDeclaredFields() ) {
if ( isFieldSupported.test(field) ) {
field.setAccessible(true);
// Recursively do the same for all the fields
final Object value = create(field.getGenericType(), defaultInstanceSupplier, isTypeSupported, isFieldSupported, objectCreator, postProcess);
field.set(newT, value);
}
}
}
// And then do post-processing for these "special" types
@SuppressWarnings("unchecked")
final T castNewT = (T) postProcess.apply(newT, type);
return castNewT;
}
static Optional<?> supplyDefaultInstance(final Type type) {
final Optional<?> defaultValueFromIndex = defaultObjectsByIndex.get(type);
if ( defaultValueFromIndex != null ) {
return defaultValueFromIndex;
}
return defaultObjectsByFirstBest
.stream()
.map(resolver -> resolver.apply(type))
.filter(Optional::isPresent)
.findFirst()
.orElse(Optional.empty());
}
private static Function<? super Type, Optional<?>> ifAssignable(final Class<?> expectedClass, final Supplier<?> defaultObject) {
return actualClass -> expectedClass.isAssignableFrom(Types.typeToClass(actualClass))
? Optional.of(defaultObject.get())
: Optional.empty();
}
}
一旦你拥有了这一切,你就可以拥有一个 Gson 特化并且进行后处理,比如将模拟元素添加到集合中(集合默认是并且应该是空的)。
public final class Q50515517 {
private Q50515517() {
}
private static final Gson gson = new GsonBuilder()
.serializeNulls()
.setPrettyPrinting()
.disableHtmlEscaping()
.create();
// Gson can avoid use of constructors
private static final UnsafeAllocator unsafeAllocator = UnsafeAllocator.create();
public static void main(final String... args)
throws Exception {
gson.toJson(create(Object3.class, gson, Q50515517::customPostProcess), Object3.class, System.out);
}
// Here we make some Gson adaptations
private static <T> T create(final Type type, final Gson gson, final BiFunction<Object, Type, Object> postProcess)
throws Exception {
final Excluder excluder = gson.excluder();
final Predicate<? super Type> isClassSupported = t -> !excluder.excludeClass(Types.typeToClass(t), true);
final Predicate<? super Field> isFieldSupported = field -> !excluder.excludeField(field, true);
return Mock.create(type, Mock::supplyDefaultInstance, isClassSupported, isFieldSupported, Q50515517::createUnsafely, postProcess);
}
private static <T> T createUnsafely(final Class<T> clazz) {
try {
return unsafeAllocator.newInstance(clazz);
} catch ( final Exception ex ) {
throw new RuntimeException(ex);
}
}
private static Object customPostProcess(final Object object, final Type type) {
if ( object instanceof Collection ) {
if ( type instanceof ParameterizedType ) {
@SuppressWarnings("unchecked")
final Collection<Object> collection = (Collection<Object>) object;
final ParameterizedType parameterizedType = (ParameterizedType) type;
final Type elementType = parameterizedType.getActualTypeArguments()[0];
try {
final Object newElement = create(elementType, gson, Q50515517::customPostProcess);
collection.add(newElement); // This is where we add a mock element to the collection
return object;
} catch ( final Exception ex ) {
throw new RuntimeException(ex);
}
}
}
return object;
}
}
综上所述,该解决方案支持:
- 没有默认构造函数的类
- 对象类层次结构字段
- 排除策略
- 接口
- 等
上面测试中的示例类:
class Object1 {
String s1;
}
class Object2
extends Object1 {
String s2;
}
class Object3
extends Object2 {
String s3;
List<Object4> lo4;
}
class Object4 {
String s4a;
String s4b;
}
输出:
{
"s3": "",
"lo4": [
{
"s4a": "",
"s4b": ""
}
],
"s2": "",
"s1": ""
}