如何将对象序列化为字符串


150

我能够将一个对象序列化为一个文件,然后再次还原它,如下面的代码片段所示。我想将对象序列化为字符串并存储到数据库中。谁能帮我?

LinkedList<Diff_match_patch.Patch> patches = // whatever...
FileOutputStream fileStream = new FileOutputStream("foo.ser");
ObjectOutputStream os = new ObjectOutputStream(fileStream);
os.writeObject(patches1);
os.close();

FileInputStream fileInputStream = new FileInputStream("foo.ser");
ObjectInputStream oInputStream = new ObjectInputStream(fileInputStream);
Object one = oInputStream.readObject();
LinkedList<Diff_match_patch.Patch> patches3 = (LinkedList<Diff_match_patch.Patch>) one;
os.close();

Answers:


270

塞尔吉奥:

您应该使用BLOB。使用JDBC非常简单。

您发布的第二个代码的问题是编码。您还应该对字节进行编码,以确保它们均不会失败。

如果您仍然想将其记录为字符串,则可以使用java.util.Base64编码字节。

仍然应该将CLOB用作数据类型,因为您不知道序列化数据将持续多长时间。

这是一个如何使用它的示例。

import java.util.*;
import java.io.*;

/** 
 * Usage sample serializing SomeClass instance 
 */
public class ToStringSample {

    public static void main( String [] args )  throws IOException,
                                                      ClassNotFoundException {
        String string = toString( new SomeClass() );
        System.out.println(" Encoded serialized version " );
        System.out.println( string );
        SomeClass some = ( SomeClass ) fromString( string );
        System.out.println( "\n\nReconstituted object");
        System.out.println( some );


    }

    /** Read the object from Base64 string. */
   private static Object fromString( String s ) throws IOException ,
                                                       ClassNotFoundException {
        byte [] data = Base64.getDecoder().decode( s );
        ObjectInputStream ois = new ObjectInputStream( 
                                        new ByteArrayInputStream(  data ) );
        Object o  = ois.readObject();
        ois.close();
        return o;
   }

    /** Write the object to a Base64 string. */
    private static String toString( Serializable o ) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        ObjectOutputStream oos = new ObjectOutputStream( baos );
        oos.writeObject( o );
        oos.close();
        return Base64.getEncoder().encodeToString(baos.toByteArray()); 
    }
}

/** Test subject. A very simple class. */ 
class SomeClass implements Serializable {

    private final static long serialVersionUID = 1; // See Nick's comment below

    int i    = Integer.MAX_VALUE;
    String s = "ABCDEFGHIJKLMNOP";
    Double d = new Double( -1.0 );
    public String toString(){
        return  "SomeClass instance says: Don't worry, " 
              + "I'm healthy. Look, my data is i = " + i  
              + ", s = " + s + ", d = " + d;
    }
}

输出:

C:\samples>javac *.java

C:\samples>java ToStringSample
Encoded serialized version
rO0ABXNyAAlTb21lQ2xhc3MAAAAAAAAAAQIAA0kAAWlMAAFkdAASTGphdmEvbGFuZy9Eb3VibGU7T
AABc3QAEkxqYXZhL2xhbmcvU3RyaW5nO3hwf////3NyABBqYXZhLmxhbmcuRG91YmxlgLPCSilr+w
QCAAFEAAV2YWx1ZXhyABBqYXZhLmxhbmcuTnVtYmVyhqyVHQuU4IsCAAB4cL/wAAAAAAAAdAAQQUJ
DREVGR0hJSktMTU5PUA==


Reconstituted object
SomeClass instance says: Don't worry, I'm healthy. Look, my data is i = 2147483647, s = ABCDEFGHIJKLMNOP, d = -1.0

注意:对于Java 7和更早版本,您可以在此处看到原始答案


如果您确实需要字符串,则+1,然后使用base64 + clob即可。
John Gardner

6
+1,小改进。最好在toString()方法中使用接口Serializable而不是普通对象:私有静态字符串toString(Serializable对象)
tefozi

4
如果我们尝试将对象存储为字节数组而不是字符串,则无需使用BASE64就可以实现相同的目的。
Sudar 2010年

2
致命的缺陷是类定义会随着时间而改变-如果发生这种改变,您将无法反序列化!添加serialVersionUIDto SomeClass可以防止添加新字段,但是如果删除了字段,则会很麻烦。这是值得一读约书亚布洛赫具有有效的Java这一说- books.google.co.uk/...
尼克·霍尔特

1
从Java 8开始,现在有了java.util.Base64。您应该更新答案:Base64.getEncoder()。encodeToString(baos.toByteArray()); Base64.getDecoder()。decode(s);
drUniversalis 2015年

12

如何将数据写入ByteArrayOutputStream而不是FileOutputStream?

否则,您可以使用XMLEncoder序列化对象,保留XML,然后通过XMLDecoder反序列化。


8

感谢您的迅速答复。我会在中间放弃一些票,以感谢您的帮助。根据您的回答,我认为最好的解决方案已经编码。

LinkedList<Patch> patches1 = diff.patch_make(text2, text1);
try {
    ByteArrayOutputStream bos = new ByteArrayOutputStream();
    ObjectOutputStream os = new ObjectOutputStream(bos);
    os.writeObject(patches1);
    String serialized_patches1 = bos.toString();
    os.close();


    ByteArrayInputStream bis = new ByteArrayInputStream(serialized_patches1.getBytes());
    ObjectInputStream oInputStream = new ObjectInputStream(bis);
    LinkedList<Patch> restored_patches1 = (LinkedList<Patch>) oInputStream.readObject();            



        // patches1 equals restored_patches1
    oInputStream.close();
} catch(Exception ex) {
    ex.printStackTrace();
}

注意我没有考虑使用JSON,因为效率较低。

注意:我将考虑您的建议,即不要将序列化的对象作为字符串存储在数据库中,而将byte []存储在数据库中。


3
“ ByteArrayOutputStream.toString使用平台默认编码进行转换。您确定要这样做吗?特别是因为任意字节数组不是有效的UTF8。此外,数据库将对其进行处理。”
汤姆·霍顿

您应该认真对待Tom Hawtin的上述评论
-anjanb

更不用说长期存储序列化的对象不是一个好主意,也不建议使用
Steve g

“请注意,我不考虑使用JSON,因为效率较低。” 如何使用Google的协议缓冲区来提高效率?同样,史蒂夫·克的想法也很合理。协议缓冲区是将序列化数据存储在DB中但仍可用于Java以外的其他语言的一种方法。
anjanb

5

Java8方法,受OscarRyz的回答启发,将Object从String转换为String 。对于解码/编码,必须使用java.util.Base64

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Base64;
import java.util.Optional;

final class ObjectHelper {

  private ObjectHelper() {}

  static Optional<String> convertToString(final Serializable object) {
    try (final ByteArrayOutputStream baos = new ByteArrayOutputStream();
      ObjectOutputStream oos = new ObjectOutputStream(baos)) {
      oos.writeObject(object);
      return Optional.of(Base64.getEncoder().encodeToString(baos.toByteArray()));
    } catch (final IOException e) {
      e.printStackTrace();
      return Optional.empty();
    }
  }

  static <T extends Serializable> Optional<T> convertFrom(final String objectAsString) {
    final byte[] data = Base64.getDecoder().decode(objectAsString);
    try (final ObjectInputStream ois = new ObjectInputStream(new ByteArrayInputStream(data))) {
      return Optional.of((T) ois.readObject());
    } catch (final IOException | ClassNotFoundException e) {
      e.printStackTrace();
      return Optional.empty();
    }
  }
}

为什么这是接口而不是类?
simonalexander2005 '19

@ simonalexander2005有意义的说明,我不再在这里使用接口。我改了
马库斯·舒尔特

4

XStream提供了一个简单的实用程序,用于对XML进行序列化/反序列化,并且非常快速。存储XML CLOB而不是二进制BLOBS将变得不那么脆弱,更不用说可读性了。



3

如果要将对象作为二进制数据存储在数据库中,那么您确实应该使用BLOB数据类型。该数据库能够更有效地存储它,您不必担心编码等问题。JDBC提供了根据流创建和检索Blob的方法。如果可以的话,请使用Java 6,它对JDBC API进行了一些补充,使处理blob变得更加容易。

如果您绝对需要将数据存储为String,我建议使用XStream进行基于XML的存储(比容易得多XMLEncoder),但是替代对象表示可能同样有用(例如JSON)。您的方法取决于您为什么实际需要以这种方式存储对象。


2

看看java.sql.PreparedStatement类,特别是该函数

http://java.sun.com/javase/6/docs/api/java/sql/PreparedStatement.html#setBinaryStream(int,%20java.io.InputStream)

然后看一下java.sql.ResultSet类,特别是该函数

http://java.sun.com/javase/6/docs/api/java/sql/ResultSet.html#getBinaryStream(int)

请记住,如果要将对象序列化到数据库中,然后以新版本更改代码中的对象,则反序列化过程很容易失败,因为对象的签名已更改。我曾经通过存储序列化的自定义“首选项”,然后对“首选项”定义进行更改而犯了这个错误。突然我看不到任何以前的序列化信息。

为避免在对象版本和反序列化方面出现此问题,您最好在表中编写笨拙的每个属性列,然后以这种方式组成和分解对象。或将属性写入某种哈希表(例如java.util.Properties对象),然后序列化极不可能更改的属性对象。


1

序列化的流只是一个字节序列(八位字节)。因此,问题是如何将字节序列转换为String,然后再次转换为String。此外,如果要将其存储在数据库中,则需要使用一组有限的字符代码。

解决该问题的明显方法是将字段更改为二进制LOB。如果要坚持使用字符型LOB,则需要以某种方案(例如base64,hex或uu)进行编码。


1

您可以使用类sun.misc.Base64Decoder和sun.misc.Base64Encoder中的构建将序列化的二进制数据转换为字符串。您不需要其他类,因为它是内置的。



0

简单的解决方案,为我工作

public static byte[] serialize(Object obj) throws IOException {
    ByteArrayOutputStream out = new ByteArrayOutputStream();
    ObjectOutputStream os = new ObjectOutputStream(out);
    os.writeObject(obj);
    return out.toByteArray();
}

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.