37

How to exclude class field from serialization process in runtime ? There is transient modifier for compilation time but what about runtime ? I mean common java serialization with ObjectOutputStream, not gson or something.

Sorry I think I didn't explain right. This is not exactly about serialization , but about de-serialization. I have batch of legacy files and handle them like this:

public class Deserialize {

/**
 * @param args
 * @throws IOException 
 * @throws ClassNotFoundException 
 */
public static void main(String[] args) throws ClassNotFoundException, IOException {
    File file = new File("/home/developer/workspace/DDFS/some.ddf");
    HackedObjectInputStream in = new HackedObjectInputStream(new GZIPInputStream(new FileInputStream(file)));

    System.out.println("Attempt to open " + file.getAbsolutePath());
    Object obj = in.readObject();
    in.close();


}

 static class HackedObjectInputStream extends ObjectInputStream
    {

        /**
         * Migration table. Holds old to new classes representation.
         */
        private static final Map<String, Class<?>> MIGRATION_MAP = new HashMap<String, Class<?>>();

        static
        {
            MIGRATION_MAP.put("DBOBExit", Exit.class);
        }

        /**
         * Constructor.
         * @param stream input stream
         * @throws IOException if io error
         */
        public HackedObjectInputStream(final InputStream stream) throws IOException
        {
            super(stream);
        }

        @Override
        protected ObjectStreamClass readClassDescriptor() throws IOException, ClassNotFoundException
        {
            ObjectStreamClass resultClassDescriptor = super.readClassDescriptor();

            for (final String oldName : MIGRATION_MAP.keySet())
            {
                if (resultClassDescriptor.getName().equals(oldName))
                {
                    resultClassDescriptor = ObjectStreamClass.lookup(MIGRATION_MAP.get(oldName));   
                }
            }

            return resultClassDescriptor;
        }

    }

}

This code works fine for most of files , but some files throws

Exception in thread "main" java.lang.ClassCastException: cannot assign instance of java.awt.Polygon to field Exit.msgbackPt of type java.awt.Point in instance of Exit
at java.io.ObjectStreamClass$FieldReflector.setObjFieldValues(ObjectStreamClass.java:2053)

because of different versions of Exit class . New version has new fields. Error disappearing when I add transient to new fields, but another files starts to throwing an exception (latest files).

So can I add transient to these new fileds in runtime if I detect legacy serilized file ? Maybe reflection or something ?

gaponov
  • 1,012
  • 1
  • 12
  • 22
  • You need to either implement `writeObject()` and `readObject()` yourself, or maybe use `Externalizable` instead of `Serializable` which is supposed to give you full control over the process: http://docs.oracle.com/javase/7/docs/platform/serialization/spec/serial-arch.html#4539 (and other parts of that spec.) Do note however that you'll probably have to correctly de-serialize your whole class that way, and that you'll have to handle figuring out what fields you've written during serialisation when deserialising. (By writing a bunch of flags or such before the data.) – millimoose Jan 29 '13 at 12:21
  • Why? What's supposed to happen at the other end? – user207421 Jan 29 '13 at 13:08
  • Looking at the edit: I think you've got your work cut out of you. Builtin serialisation is a really, really clunky choice if you want to persist your data and evolve its schema. – millimoose Jan 30 '13 at 01:43
  • I found the solution in simple way http://stackoverflow.com/a/14608062/1085787 – gaponov Jan 30 '13 at 15:58

4 Answers4

63

The documentation of ObjectOutputStream says:

The default serialization mechanism for an object writes the class of the object, the class signature, and the values of all non-transient and non-static fields. References to other objects (except in transient or static fields) cause those objects to be written also.

So when you declare a variable as transient, it should be ignored by ObjectOutputStream. Make sure that you use the transient keyword and not a @Transient annotation. Such annotations are used by some ORM frameworks to mark fields which are not supposed to be saved in databases. They are meaningless for the buildin serialization framework.

private transient String foo; // Field gets ignored by ObjectOutputStream
@Transient private String bar; // Treated normally by ObjectOutputStream (might mean something for some other framework)
Philipp
  • 67,764
  • 9
  • 118
  • 153
  • 1
    Example seems to be wrong. Transient fields should be ignored not the Non-Transient. – BlueM Jul 12 '16 at 12:52
  • 1
    @BlueM That's what I wrote: "when you declare a variable as transient, it should be ignored by ObjectOutputStream" – Philipp Jul 12 '16 at 13:24
4

You can use the transient modifier:

http://docs.oracle.com/javase/specs/jls/se7/html/jls-8.html#jls-8.3.1.3

0

The serialized representation of a particular class is up to the class itself, you can't change it externally, the nearest you can get is to define a subclass with custom serialization behaviour but that only affects objects of that subclass, not objects of the parent type.

If you can't modify the class in question at all then your only option is to subclass ObjectOutputStream and override replaceObject to replace the problem object at write time with a different one that contains only the data you want, and the mirror image process at read time (subclass ObjectInputStream and override resolveObject).

Ian Roberts
  • 120,891
  • 16
  • 170
  • 183
0

You're digging the wrong hole here. Instead of messing around with runtime decisions about which fields to serialized and overriding readClassDescriptor(), you should look into overriding readResolve().

user207421
  • 305,947
  • 44
  • 307
  • 483
  • Does it mean what I have to add both classes to project, old one and new one version ? For which class readResolve should be overriden ?Whole ticket about deserialization of modified class (was moved to different package and renamed). – gaponov Jan 30 '13 at 13:12