Search code examples
javaandroidserializationhashsetormlite

Why is my HashSet being serialized incorrectly in ORMLite? It appears to be serialized with the wrong size


I am saving a field in my app as a HashSet<String>, defined as such:

@DatabaseField(dataType = DataType.SERIALIZABLE)
private HashSet<String> allUsers;

Some devices are occasionally having an error where this field is serialized with the incorrect size byte, and thus it cannot be deserialized. Example error message with dummy data:

java.sql.SQLException: Could not read serialized object from byte array: [-84,
  -19, 0, 5, 115, 114, 0, 17, 106, 97, 118, 97, 46, 117, 116, 105, 108, 46, 72,
  97, 115, 104, 83, 101, 116, -70, 68, -123, -107, -106, -72, -73, 52, 3, 0, 0,
  120, 112, 119, 12, 0, 0, 0, 16, 63, 64, 0, 0, 0, 0, 0, /* this is the problem
  byte */ 3, 116, 0, 4, 115, 111, 109, 101, 116, 0, 4, 100, 97, 116, 97, 120]

If I correct the size byte to create a new array like so:

[-84, -19, 0, 5, 115, 114, 0, 17, 106, 97, 118, 97, 46, 117, 116, 105, 108, 46,
  72, 97, 115, 104, 83, 101, 116, -70, 68, -123, -107, -106, -72, -73, 52, 3, 0,
  0, 120, 112, 119, 12, 0, 0, 0, 16, 63, 64, 0, 0, 0, 0, 0, /* correct size is
  2 */ 2, 116, 0, 4, 115, 111, 109, 101, 116, 0, 4, 100, 97, 116, 97, 120]

I can then deserialize the array to find a HashSet containing two strings.

I want to know possible reasons for a HashSet being serialized with the wrong size byte in this way. I have not been able to determine steps to reproduce this issue but it has happened on several devices. However, the vast majority of devices this app runs on do not have this issue.

I have tried reproducing the issue by serializing a HashSet, then deserializing it, adding and removing objects, and serializing again. But I was not able to reproduce the issue. I want to know if there is a known reason for HashSets in Java to be serialized incorrectly like this.


Solution

  • Some devices are occasionally having an error where this field is serialized with the incorrect size byte, and thus it cannot be deserialized.

    My best guess is that something is modifying the HashSet in another thread. All ORMLite is doing is serializing the data into a buffer and then storing it as a bag o' bytes. Check out the rather simple code from SerializableType.sqlArgToJava(...). It's basically doing:

    ByteArrayOutputStream outStream = new ByteArrayOutputStream();
    objOutStream = new ObjectOutputStream(outStream);
    objOutStream.writeObject(obj);
    objOutStream.close();
    return outStream.toByteArray();
    

    As you mention, the serialized HashSet says that its size is 3 however there are only 2 strings stored in the set. I can see that happening when an element gets added to the HashSet and the size field has been updated in the persisting thread's memory but not the underlying node array because of memory synchronization issues between threads. It's also suspicious that the serialized stream had that problem was wasn't in the middle of a string or some other data.