Как двоичный (де) сериализовать объект в строку/form?
Мне нужно сериализовать объекты в String и десериализовать.
Я прочитал sugestion on stackoverflow и сделаю этот код:
class Data implements Serializable {
int x = 5;
int y = 3;
}
public class Test {
public static void main(String[] args) {
Data data = new Data();
String out;
try {
// zapis
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(data);
out = new String(baos.toByteArray());
System.out.println(out);
// odczyt.==========================================
ByteArrayInputStream bais = new ByteArrayInputStream(out.getBytes());
ObjectInputStream ois = new ObjectInputStream(bais);
Data d = (Data) ois.readObject();
System.out.println("d.x = " + d.x);
System.out.println("d.y = " + d.y);
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
но я получаю ошибку:
java.io.StreamCorruptedException: invalid stream header: EFBFBDEF
at java.io.ObjectInputStream.readStreamHeader(ObjectInputStream.java:801)
at java.io.ObjectInputStream.<init>(ObjectInputStream.java:298)
at p.Test.main(Test.java:37)
Почему?
Я ожидал:
d.x = 5
d.y = 3
как это сделать в хорошем смысле?
Ах. Я не хочу писать этот объект в файл. Я должен иметь его в строчном формате.
Ответы
Ответ 1
Используйте
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray());
вместо
ByteArrayInputStream bais = new ByteArrayInputStream(out.getBytes());
, поскольку преобразование String искажает данные (из-за кодировки).
Если вам действительно нужно сохранить результат в String, вам нужен безопасный способ хранения произвольных байтов в String. Один из способов сделать это для нас Base64-кодирование.
Совершенно другой подход состоял бы в том, чтобы не использовать стандартную сериализацию Java для этого класса, а создавать собственный конвертер данных в/из String.
Ответ 2
Не совсем верно сказать, что преобразование в строку искажает данные. Преобразование в "UTF-8" происходит потому, что оно не является биективным (некоторые символы имеют 2 байта, но не все 2 байтовые последовательности разрешены в виде последовательностей символов), тогда как "ISO-8859-1" является биективным (1 символ строки является байт и наоборот).
Кодирование Base64 не очень экономично по сравнению с этим.
Вот почему я бы рекомендовал:
/**
* Serialize any object
* @param obj
* @return
*/
public static String serialize(Object obj) {
try {
ByteArrayOutputStream bo = new ByteArrayOutputStream();
ObjectOutputStream so = new ObjectOutputStream(bo);
so.writeObject(obj);
so.flush();
// This encoding induces a bijection between byte[] and String (unlike UTF-8)
return bo.toString("ISO-8859-1");
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* Deserialize any object
* @param str
* @param cls
* @return
*/
public static <T> T deserialize(String str, Class<T> cls) {
// deserialize the object
try {
// This encoding induces a bijection between byte[] and String (unlike UTF-8)
byte b[] = str.getBytes("ISO-8859-1");
ByteArrayInputStream bi = new ByteArrayInputStream(b);
ObjectInputStream si = new ObjectInputStream(bi);
return cls.cast(si.readObject());
} catch (Exception e) {
e.printStackTrace();
}
}