10

I am hitting a brickwall with serialization of a subclass of Location in android/java

Location is not serializable. I have a first subclass called FALocation that does not have any instance variables. I have declared it serializable.

Then I have a second class called Waypoint that looks like this:

public class Waypoint extends FALocation implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    /* Class variables *******************************************************/
    private static int CLASS_VERSION=1; //Used to version parcels

    /* Instance variables ****************************************************/
    private transient String type=DataHelper.PT_TYPE_US;
    private transient String country; 
    private transient String name=null;
    private transient String description=null;
    private transient int elevation = 0;
    private transient int population = 0; // Afterthought, added to match the DB structure

    /* Constructors **********************************************************/    
    public Waypoint() {
        super();
    }

    public Waypoint(double lat, double lon, String name, String description) {
        super(lat, lon);
        this.setName(name);
        this.setDescription(description);
    }

    public Waypoint(Location l) {
        super(l);
    }

    public Waypoint(String provider) {
        super(provider);
    }


    /* Implementing serializable */
    private void writeObject(java.io.ObjectOutputStream out) throws IOException {
        Log.v("DroidFA", "Serialising \"%s\" (v%d).", Waypoint.class.getSimpleName(), CLASS_VERSION);
        out.writeInt(CLASS_VERSION);

        out.writeObject(type);
        out.writeObject(country);
        out.writeObject(name);
        out.writeObject(description);
        out.writeInt(elevation);
        out.writeInt(population);
    }

    private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {

        int serialClassVersion = in.readInt();
        Log.v("DroidFA", "Deserialising \"%s\" (v%d).", Waypoint.class.getSimpleName(),serialClassVersion);

        type = (String) in.readObject();
        country = (String) in.readObject();
        name = (String) in.readObject();
        description = (String) in.readObject();
        elevation = in.readInt();
        population = in.readInt();
    }
}

Serialization works fine.

Deseriamization produces the followwing exception (the leg object contains a waypoint).:

10-05 13:50:35.259: WARN/System.err(7867): java.io.InvalidClassException: android.location.Location; IllegalAccessException
10-05 13:50:35.267: WARN/System.err(7867):     at java.io.ObjectInputStream.resolveConstructorClass(ObjectInputStream.java:2010)
10-05 13:50:35.267: WARN/System.err(7867):     at java.io.ObjectInputStream.readNewObject(ObjectInputStream.java:2095)
10-05 13:50:35.267: WARN/System.err(7867):     at java.io.ObjectInputStream.readNonPrimitiveContent(ObjectInputStream.java:929)
10-05 13:50:35.267: WARN/System.err(7867):     at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2285)
10-05 13:50:35.278: WARN/System.err(7867):     at java.io.ObjectInputStream.readObject(ObjectInputStream.java:2240)
10-05 13:50:35.278: WARN/System.err(7867):     at com.droidfa.navigation.Leg.readObject(Leg.java:262)
.../...
jmc34
  • 810
  • 3
  • 10
  • 22
  • It seems, that problem is somewhere else. com.droidfa.navigation.Leg.readObject(Leg.java:262) – bugs_ Oct 05 '11 at 14:23
  • Leg line 262 is : "from = (Waypoint) in.readObject();" which is where the problem lies indeed ;). – jmc34 Oct 05 '11 at 14:39
  • I needed to save `Location` with most of it's members as well, not just latitude and longitude. I ended up writing my own `Serializable` class. See my answer here: https://stackoverflow.com/a/63991813/96313 – Viktor Brešan Sep 21 '20 at 20:22

2 Answers2

11

Is it absolutely necessary to serialize the Location? maybe you could mark it as transient, and obtain it dynamically after deserializing the object. (Anyway, from the documentation ) :

Q: If class A does not implement Serializable but a subclass B implements Serializable, will the fields of class A be serialized when B is serialized?

A: Only the fields of Serializable objects are written out and restored. The object may be restored only if it has a no-arg constructor that will initialize the fields of non-serializable supertypes. If the subclass has access to the state of the superclass it can implement writeObject and readObject to save and restore that state.

So, if the subclass has access to the fields of its non-serializable superclass(es) it can use the writeObject and readObject protocol to implement serialization. Otherwise, there will be fields that won't be possible to serialize.

Community
  • 1
  • 1
Óscar López
  • 232,561
  • 37
  • 312
  • 386
  • Ok, I can serialize the Location fields from the subclass, but the issue is that it tries to deserialize the Location anyway. Is there anyway I can stop that happening from the subclass ? – jmc34 Oct 05 '11 at 16:46
  • Ok, I have "pseudo" serielized my Location subclass from the enclosing object. Solved the problem. Thanks. – jmc34 Oct 07 '11 at 07:04
  • " I have "pseudo" serielized my Location subclass from the enclosing object." Hi i am facing the same, could you please elaborate. WHat does Pseudo mean here? – stack_ved Jul 25 '14 at 07:35
1

Looks like Location does not have public/protected no-arg constructor. Such a constructor is needed for making it available for serialization in subclass.

http://download.oracle.com/javase/6/docs/api/java/io/Serializable.html says:

To allow subtypes of non-serializable classes to be serialized, the subtype may assume responsibility for saving and restoring the state of the supertype's public, protected, and (if accessible) package fields. The subtype may assume this responsibility only if the class it extends has an accessible no-arg constructor to initialize the class's state. It is an error to declare a class Serializable if this is not the case. The error will be detected at runtime.

And same with the words from Serialization specification:

A Serializable class must do the following: ... Have access to the no-arg constructor of its first nonserializable superclass

That would explain why you have problems only in deserialization, because naturally constructor is not called during serialization.

Small example of failing without accessible constructor:

public class A {
    public A(String some) {};
    private A() {} //as protected or public everything would work
}

public class B extends A implements Serializable {
    public B() {
        super("");
    }
    //these doesn't really matter
    //private void writeObject(java.io.ObjectOutputStream out) throws IOException {  }
    //private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException { }
}

public class BSerializer {

    public static void main(String ... args) throws Exception {
        B b = new B();

        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream(baos);
        oos.writeObject(b);
        ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
        ObjectInputStream ois = new ObjectInputStream(bais);
        B deserialized = (B) ois.readObject();   //InvalidClassException
    }
}
Mikko Maunu
  • 41,366
  • 10
  • 132
  • 135
  • android.location.Location; IllegalAccessException is Thrown when a program attempts to access a field or method which is not accessible from the location where the reference is made. – bugs_ Oct 05 '11 at 14:28
  • Location indeed does not have a no arg public constructor. But where should I go from there? – jmc34 Oct 05 '11 at 14:41
  • Unfortunately then you cannot serialize it directly. You cannot do anything more than save state somewhere else, take saved state and construct new Location (or whatever extends it). – Mikko Maunu Oct 05 '11 at 14:46
  • @bugs_ thats true, but it has nothing to do with this case. There is no IllegalAccessException thrown, "IllegalAccessException" was msg argument to the constructor of InvalidClassException which was created and thrown from resolveConstructorClass in ObjectInputStream class. – Mikko Maunu Oct 05 '11 at 16:21