Search code examples
javaserializationhashtableobjectinputstreamobjectoutputstream

Java - Error deserializing HashTable containing primitive type


I have serialized a HashTable<String,Object> object using an ObjectOutputStream. When serializing the object, I get no exception, but upon deserialization, the following exception occurs:

Exception in thread "main" java.io.InvalidClassException: java.lang.Long; local class
incompatible: stream classdesc serialVersionUID = 4290774032661291999, local class
serialVersionUID = 4290774380558885855

I no longer get the error when I remove all of the keys in the HashTable that have a value that is not a String (all of the key / value pairs I removed had a primitive type as their value).

What could be causing this error?

UPDATE - Here's the code

public static String serialize(Quiz quiz) throws IOException{
    HashMap<String,Object> quizData = new HashMap<String,Object>();
    quizData.put("version", 0); //int
    quizData.put("name", quiz.getName()); //String
    quizData.put("desc", quiz.getDesc()); //String
    quizData.put("timelimitType", quiz.getTimelimitType()); //String
    quizData.put("timelimit", quiz.getTimelimit()); //long
    ArrayList<String> serializedQuestionsData = new ArrayList<String>();
    for (Question question : quiz.getQuestions())
        serializedQuestionsData.add(Question.serialize(question));
    quizData.put("questions", serializedQuestionsData.toArray(new String[0])); //String[]
    ByteArrayOutputStream baos = new ByteArrayOutputStream();
    ObjectOutputStream oos;
    try { oos = new ObjectOutputStream(baos); } catch (IOException error){ throw error; }
    try { oos.writeObject(quizData); } catch (IOException error){ throw error; }
    return baos.toString();
}
@SuppressWarnings("unchecked")
public static Quiz deserialize(String serializedQuizData) throws IOException, ClassNotFoundException{
    ByteArrayInputStream bais = new ByteArrayInputStream(serializedQuizData.getBytes());
    ObjectInputStream ois;
    try { ois = new ObjectInputStream(bais); } catch (IOException error){ throw error; }
    HashMap<String,Object> quizData;
    // Exception occurs on the following line!!
    try { quizData = (HashMap<String,Object>) ois.readObject(); } catch (ClassNotFoundException error){ throw error; }
    Quiz quiz;
    if ((int) quizData.get("version") == 0){
        quiz = new Quiz((String) quizData.get("name"),
                (String) quizData.get("desc"),
                (String) quizData.get("timelimitType"),
                (long) quizData.get("timelimit"));
        for (String serializedQuestionData : (String[]) quizData.get("questions"))
            quiz.addQuestion(Question.deserialize(serializedQuestionData));
    } else {
        throw new UnsupportedOperationException("Unsupported version: \"" + quizData.get("version") + "\"");
    }
    return quiz;
}

Solution

  • The problem is that you're transforming a byte array output stream to a String using toString(). The toString() method simply uses the platform default encoding to transform the bytes (which do not represent characters at all but are purely binary data) into a String. This is thus a lossy operation, because your platform default encoding doesn't have a valid character for every possible byte.

    You shouldn't use String to hold binary data. A String contains characters. If you really need a String, then encode the byte array using a Hexadecimal or Base64 encoder. Otherwise, simply use a byte array to hold your binary data:

    public static byte[] serialize(Quiz quiz) throws IOException{
        ...
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ...
        return baos.toByteArray();
    }
    
    @SuppressWarnings("unchecked")
    public static Quiz deserialize(byte[] serializedQuizData) throws IOException, ClassNotFoundException{
        ByteArrayInputStream bais = new ByteArrayInputStream(serializedQuizData);
        ...
        return quiz;
    }