gpt4 book ai didi

java - 指定在 ObjectOutputStream 中(不)序列化哪些字段而不使用 transient 或 serialPersistentFields

转载 作者:塔克拉玛干 更新时间:2023-11-03 04:38:01 27 4
gpt4 key购买 nike

有什么办法可以告诉ObjectOutputStream应该在不使用关键字 transient 的情况下序列化可序列化类的哪些字段并且没有定义 serialPersistentFields -大批?

背景:我需要使用注释来定义类的哪些成员应该被序列化(或者更好:不被序列化)。涉及的类必须实现接口(interface)Serializable ,但不是 Externalizable ,所以我不想为每个对象实现序列化/反序列化算法,而只想为它使用注释。我无法使用 transient关键字,因为注释需要一些进一步的检查来确定一个字段是否应该被序列化。这些检查必须由 ObjectOutputStream 完成。 (或在我自己的 ObjectOutputStream 子类中)。我也无法定义 serialPersistentFields -array 在每个类中,因为如前所述,在编译时并没有定义哪些字段应该被序列化。

因此,在受影响的类中唯一应该注意的是字段级别的注释( @Target(ElementType.FIELD) )。

在过去的几天里,我尝试了很多方法,但没有找到一种有效的方法:
ObjectOutputStream有一个方法 writeObjectOverride(Object)可用于在扩展 ObjectOutputStream 时定义自己的序列化过程实现.这仅适用于 ObjectOutputStream用无参数构造函数初始化,否则 writeObjectOverride永远不会被调用。但是这种方法需要我自己实现整个序列化过程,我不想这样做,因为它非常复杂并且默认已经实现了 ObjectOutputStream .我正在寻找一种方法来修改默认的序列化实现。

另一种方法是扩展 ObjectOutputStream再次覆盖 writeObjectOverride(Object) (在拨打 enableReplaceObject(true) 之后)。在这种方法中,我尝试使用某种 SerializationProxy(请参阅 What is the Serialization Proxy Pattern? )将序列化对象封装在代理中,该代理定义了应序列化的字段列表。但是这种方法也失败了,因为 writeObjectOverride 然后也被称为代理中的字段列表( List<SerializedField> fields ),从而导致无限循环。

例子:

public class AnnotationAwareObjectOutputStream extends ObjectOutputStream {    
public AnnotationAwareObjectOutputStream(OutputStream out)
throws IOException {
super(out);
enableReplaceObject(true);
}

@Override
protected Object replaceObject(Object obj) throws IOException {
try {
return new SerializableProxy(obj);
} catch (Exception e) {
return new IOException(e);
}
}

private class SerializableProxy implements Serializable {
private Class<?> clazz;
private List<SerializedField> fields = new LinkedList<SerializedField>();

private SerializableProxy(Object obj) throws IllegalArgumentException,
IllegalAccessException {
clazz = obj.getClass();
for (Field field : getInheritedFields(obj.getClass())) {
// add all fields which don't have an DontSerialize-Annotation
if (!field.isAnnotationPresent(DontSerialize.class))
fields.add(new SerializedField(field.getType(), field
.get(obj)));
}
}

public Object readResolve() {
// TODO: reconstruct object of type clazz and set fields using
// reflection
return null;
}
}

private class SerializedField {
private Class<?> type;
private Object value;

public SerializedField(Class<?> type, Object value) {
this.type = type;
this.value = value;
}
}

/** return all fields including superclass-fields */
public static List<Field> getInheritedFields(Class<?> type) {
List<Field> fields = new ArrayList<Field>();
for (Class<?> c = type; c != null; c = c.getSuperclass()) {
fields.addAll(Arrays.asList(c.getDeclaredFields()));
}
return fields;
}

}

// I just use the annotation DontSerialize in this example for simlicity.
// Later on I want to parametrize the annotation and do some further checks
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DontSerialize {
}

当我发现可以在运行时修改修饰符时(请参阅 Change private static final field using Java reflection ),如果设置了相应的注释,我尝试在运行时设置 transient 修饰符。
不幸的是,这也不起作用,因为上一个链接中使用的方法似乎只适用于静态字段。
当使用非静态字段尝试它时,它会毫无异常(exception)地运行,但不会持久化,因为它看起来像 Field.class.getDeclaredField(...)每次调用时都返回受影响字段的新实例:
public void setTransientTest() throws SecurityException,
NoSuchFieldException, IllegalArgumentException,
IllegalAccessException {
Class<MyClass> clazz = MyClass.class;
// anyField is defined as "private String anyField"
Field field = clazz.getDeclaredField("anyField");

System.out.println("1. is "
+ (Modifier.isTransient(field.getModifiers()) ? "" : "NOT ")
+ "transient");

Field modifiersField = Field.class.getDeclaredField("modifiers");
boolean wasAccessible = modifiersField.isAccessible();
modifiersField.setAccessible(true);
modifiersField.setInt(field, field.getModifiers() | Modifier.TRANSIENT);
modifiersField.setAccessible(wasAccessible);

System.out.println("2. is "
+ (Modifier.isTransient(field.getModifiers()) ? "" : "NOT ")
+ "transient");

Field field2 = clazz.getDeclaredField("anyField");

System.out.println("3. is "
+ (Modifier.isTransient(field2.getModifiers()) ? "" : "NOT ")
+ "transient");
}

输出是:
1. is NOT transient
2. is transient
3. is NOT transient

因此,在再次调用 getDeclaredField ( Field field2 = clazz.getDeclaredField("anyField"); ) 后,它已经丢失了 transient 修饰符。

下一个方法:
扩展 ObjectOutputStream并覆盖 ObjectOutputStream.PutField putFields()并定义自己的 PutField 实现。 PutField 允许您指定序列化哪些(附加)字段,但不幸的是,该接口(interface)只有许多形式为 put(String name, <type> val) 的方法。并且在实现这些时,我无法将方法调用与调用它的类字段相关联。例如,当序列化声明为 private String test = "foo" 的字段时方法 put("test", "foo")被调用,但我无法关联 name 的值(即 test ),类包含字段 test因为没有对包含类的引用可用,因此无法阅读字段 test 的注释。 .

我还尝试了其他一些方法,但正如已经提到的,除了带有注释 DontSerialize 的字段之外,我无法成功序列化所有字段。展示。

我还遇到的一件事是 ByteCode 操纵器。也许这些是可能的,但我要求不使用任何外部工具 - 它需要是纯 Java(1.5 或 1.6)。

很抱歉这篇很长的帖子,但我只是想展示我已经尝试过的东西,并希望有人能帮助我。
提前致谢。

最佳答案

我会重新考虑“序列化”是否真的是你想做的事情。鉴于序列化规则依赖于在运行时定义的一些逻辑,反序列化过程将是一场噩梦。

有趣的问题,虽然。

关于java - 指定在 ObjectOutputStream 中(不)序列化哪些字段而不使用 transient 或 serialPersistentFields,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5259647/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com