Search code examples
javaserializationiodeserializationbackwards-compatibility

Java Serialization for Dummies


I'm still working on the project I already needed a bit of help with: JavaFX - TableView doesn't update items

Now I want to understand how this whole Serialization process in Java works, because unfortunately, I don't really get it now.

Before I go on, first of all, I'm a student, I'm not a professional. Second, I'm neither familiar with using DBs, nor XML or JSON, so I'd just like to find solution to my approach, no matter how inelegant it might be in the end, it just needs to work. So please don't feel offended if I just reject any advice in using other techniques.

So here's what I want: Saving three different class objects to separate files BUT maintaining backward compatibility to each of it. The objects are Settings, Statistics and a "database" object, containing all words in a list added to it. In the future I may add more statistics or settings, means adding new variables, mostly type of IntegerProperty or DoubleProperty.

Now the question is: is it possible to load old version saved files and then during the process just initiate new variables not found in the old version with just null but keep the rest as it has been saved?

All I know is that the first thing to do so is not to alter the serialVersionUID.


Another thing would be saving the whole Model object (which contains the three objects mentioned before), so I just have to implement stuff for one class instead of three. But how would that work then concerning backward compatibility? I mean the class itself would not change but it's attributes in their own class structure.


Finally, what approach should I go for? And most of all, how do I do this and maintaning backward compatibilty at the same time? I do best with some concrete examples rather than plain theory.

Here are two example methods, if it's of any help. I already have methods for each class to write and read an object.

public static void saveModel(Model model, String destination) throws IOException
{
    try 
    {
        fileOutput = new FileOutputStream(destination);
        objectOutput = new ObjectOutputStream(fileOutput);
        objectOutput.writeObject(model);
    }
    catch (IOException e) 
    {
      e.printStackTrace();
    }
    finally
    {
      if (objectOutput != null) 
      try 
      { 
           objectOutput.close(); 
      } 
      catch (IOException e) {}

      if (fileOutput != null) 
      try 
      { 
          fileOutput.close(); 
      } 
      catch (IOException e) {}
    }
}

public static Settings readSettings(String destination) throws IOException, FileNotFoundException
{
    Settings s = null;

    try 
    {
        fileInput = new FileInputStream(destination);
        objectInput = new ObjectInputStream(fileInput);

        Object obj = objectInput.readObject();

        if (obj instanceof Settings) 
        {
            s = (Settings)obj;  
        }
    }
    catch (IOException e) 
    {
        e.printStackTrace();
    }
    catch (ClassNotFoundException e) 
    {
        e.printStackTrace();
    }
    finally 
    {
        if (objectInput != null) try { objectInput.close(); } catch (IOException e) {}
        if (fileInput != null) try { fileInput.close(); } catch (IOException e) {}
    }

    return s;
}

Tell me if you need more of my current code.

Thank you in advance!


Solution

  • ... you must be this tall

    Best advice for Serialisation is to avoid it for application persistence, especially if backwards compatibility is desired property in your application.

    Answers

    Is it possible to load old version saved files and then during the process just initiate new variables not found in the old version with just null but keep the rest as it has been saved?

    Yes. Deserialising objects saved using previous versions of the class into a new version of this class will work only if:

    • fully qualified name of the class has not changed (same name and package)
    • previous and current class have exactly the same serialVersionUID; if one of the versions is missing it, it will be calculated as a 'hash' of all fields and methods and upon a mismatch deserialisation will fail.
    • inheritance hierarchy has not changed for that class (the same ancestors)
    • no fields have been removed in the new version of the class
    • no fields have become static
    • no fields have become transient

    I just have to implement stuff for one class instead of three. But how would that work then concerning backward compatibility?

    Yes. Providing that all classes of all fields of Model and Model class itself adhere to the rules above.

    Finally, what approach should I go for? And most of all, how do I do this and maintaning backward compatibilty at the same time?

    Yes, as long as you can guarantee all of the above rules forever, you will be backwards compatible.

    I am sure you can appreciate that forever, or even for next year can be very hard to guarantee, especially in software.

    This is why people do application persistence using more robust data exchange formats, than binary representation of serialised Java objects.

    Raw data for the table, could be saved using anything from CSV file to JSON docs stored as files or as documents in NoSQL database.

    For settings / config have a look at Java's Properties class which could store and load properties to and from *.properties or *.xml files or separately have a look at YAML.

    Finally for backwards compatibility, have a look at FlatBuffers

    The field of application persistence is very rich and ripe, so happy exploring.