【问题标题】:How to binary (de)serialize object into/form string?如何将对象二进制(反)序列化为/形成字符串?
【发布时间】:2012-11-26 15:46:48
【问题描述】:

我需要将对象序列化为字符串并反序列化。

我在 stackoverflow 上阅读了 sugestion 并编写了以下代码:

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

如何做好呢? 啊。我不想将此对象写入文件中。我必须以字符串格式保存它。

【问题讨论】:

  • 为什么你首先要在字符串中存储二进制表示?为什么不将其保留为字节数组或其他东西?...
  • @CostiCiudatu 因为我必须编写一个将对象存储到 sqlite 数据库中的方法,但 sqlite 中的一部分并不取决于我。并且有文本栏。现在我有 xml 序列化,但速度很慢。我需要快速的方法。
  • 你检查过 SQLite 是否支持像 BLOB 这样的东西来存储原始字节吗?
  • @CostiCiudatu Sqlite 支持 BLOB 但它对我有什么帮助?

标签: java string serialization


【解决方案1】:

说转换为字符串会破坏数据并不完全正确。转换为“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();
    }
}

【讨论】:

  • 如何确定二进制表示可以转换为String?例如,如何将String 终止符(通常在C 中的\0)转换为Stringinside 的有效字符?答案是,你不能。出于这个原因,我们应该将字节数组转换为安全的文本表示,例如 Base64。
【解决方案2】:

使用
ByteArrayInputStream bais = new ByteArrayInputStream(baos.toByteArray()); 代替 ByteArrayInputStream bais = new ByteArrayInputStream(out.getBytes());,因为字符串转换损坏了数据(因为编码)。

如果您确实需要将结果存储在 String 中,则需要一种安全的方式将任意字节存储在 String 中。一种方法是对我们进行 Base64 编码。

一种完全不同的方法是不对此类使用标准 Java 序列化,而是创建您自己的 Data to/from String 转换器。

【讨论】:

  • +1 不要忘记关闭流及其缓冲。 ;)
  • Base64 减缓了这个问题。 :)
  • 谢谢,我应该知道这是一个编码问题。在我的情况下,我需要使用 org.apache.tomcat.util.codec.binary.Base64 和 Base64.encodeBase64String 来创建字符串和 Base64.decodeBase64 来解码它。
猜你喜欢
  • 1970-01-01
  • 2022-11-15
  • 2018-11-01
  • 1970-01-01
  • 2020-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多