Indicate which fields are (not) serialized in ObjectOutputStream without using transient or serialPersistentFields

Is it possible to indicate ObjectOutputStreamwhich fields of the serializable class should be serialized without using the keyword transientand without defining serialPersistentFields-array?


Reference Information. I need to use annotations to determine which members of the class should be serialized (or better: not to be serialized). The classes involved must implement the interface Serializable, but NOT Externalizable, so I do not want to implement a serialization / deserialization algorithm for each object, but just use annotations for it. I cannot use the keyword transientbecause annotation requires some additional checks to determine if the field should be serialized or not. These checks should be done with ObjectOutputStream(or in my own subclass ObjectOutputStream). I also can not define a serialPersistentFields-array in each class, because, as explained earlier, at compile time it is not determined which fields should be serialized.

So, the only thing that should be notes in the affected class is the annotation at the field level ( @Target(ElementType.FIELD)).

In the past few days, I have tried many approaches, but have not found what works:


ObjectOutputStreamhas a method writeObjectOverride(Object)that can be used to determine your own implementation of the serialization process during expansion ObjectOutputStream. This only works if it is ObjectOutputStreaminitialized by the no-argument-constructor because it is writeObjectOverridenever called otherwise . But this approach requires that I myself implement the entire serialization process, and I do not want to do this, because it is quite complex and already implemented by default ObjectOutputStream. I am looking for a way to just change the default serialization implementation.


ObjectOutputStream writeObjectOverride(Object) ( enableReplaceObject(true)). - SerializationProxy (. - Serialization?), -, , . , 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 {
}

, (. Java) . , , , , , . , , 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");) .


:
ObjectOutputStream ObjectOutputStream.PutField putFields() PutField. PutField , () , , , put(String name, <type> val), , . , , private String test = "foo", put("test", "foo"), name ( test) , test, , test.


, , , , , DontSerialize.

, , - ByteCode. , , - - Java (1.5 1.6).


, , , , - . .

+7
3

, "" - , . , Serialization , , Deserialization .

, .

0

, , ObjectInputStream.readClassDescriptor ObjectStreamClass.

ObjectStreamClass . , .

  • , JVM. ObjectStreamClass.lookup.
  • , . ObjectInputStream.readClassDescriptor.

super.readClassDescriptor. . , .

ObjectStreamClass? ObjectOutputStream. , . ObjectInputStream readClassDescriptor readClassDescriptor .

0

Without rewriting much of Java serialization, you will need to rewrite the bytecode. At run time, this can be done with Java agents, but can also be done for class files at build time.

0
source

All Articles