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

Posted

技术标签:

【中文标题】如何将对象序列化为字符串【英文标题】:How to serialize an object into a string 【发布时间】:2010-09-13 03:58:37 【问题描述】:

我能够将一个对象序列化为一个文件,然后再次恢复它,如下一个代码 sn-p 所示。我想将对象序列化为字符串并存储到数据库中。谁能帮帮我?

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();

【问题讨论】:

【参考方案1】:

塞尔吉奥:

您应该使用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 及更早版本,您可以看到原始的 answer here

【讨论】:

+1,小改进。在 toString() 方法中最好使用接口 Serializable 而不是普通的 Object:private static String toString(Serializable object) 如果我们尝试将对象存储为字节数组而不是字符串,那么我们可以在不使用 BASE64 的情况下实现同样的效果。 其中的致命缺陷是类定义会随着时间而改变——如果发生这种改变,您将无法反序列化!将serialVersionUID 添加到SomeClass 将防止添加新字段,但如果删除字段,您将被搞砸。值得一读 Joshua Bloch 在 Effective Java 中对此的看法 - books.google.co.uk/… 从 Java 8 开始,现在有了 java.util.Base64。您应该更新您的答案: Base64.getEncoder().encodeToString(baos.toByteArray()); Base64.getDecoder().decode(s); @OscarRyz 伙计,你的回答拯救了我的一天。我做错了。最后。谢谢。【参考方案2】:

将数据写入 ByteArrayOutputStream 而不是 FileOutputStream 怎么样?

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

【讨论】:

【参考方案3】:

感谢您的快速回复。我将立即放弃一些投票以感谢您的帮助。我根据您的回答编写了我认为的最佳解决方案。

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[] 的建议。

【讨论】:

"ByteArrayOutputStream.toString 使用 平台默认编码进行转换。您确定要这样做吗?尤其是任意字节数组不是有效的 UTF8。此外,数据库正在运行破坏它。” 你应该认真对待 Tom Hawtin 的上述评论 更不用说长期存储序列化对象不是一个好主意,也不推荐 "注意我没有考虑使用 JSON,因为效率较低。"使用谷歌的协议缓冲区来提高效率怎么样?此外,Steve g 的想法非常合理。将序列化数据存储在数据库中并使其可用于除 java 之外的其他语言的一种方法再次是协议缓冲区。【参考方案4】:

Java8 方法,将 Object 从/转换为 String,灵感来自 OscarRyz 的回答。对于解码/编码,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 有意义的注释,我不会再在这里使用接口了。我改了。【参考方案5】:

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

【讨论】:

【参考方案6】:

如何将对象持久化为blob

【讨论】:

修复链接,好吗?【参考方案7】:

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

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

【讨论】:

【参考方案8】:

看一下 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 对象,然后序列化极不可能更改的属性对象。

【讨论】:

【参考方案9】:

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

该问题的明显解决方案是将字段更改为二进制 LOB。如果您想坚持使用字符 LOB,那么您需要使用一些方案进行编码,例如 base64、hex 或 uu。

【讨论】:

【参考方案10】:

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

【讨论】:

【参考方案11】:

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

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

【讨论】:

【参考方案12】:

你可以使用 UUEncoding

【讨论】:

【参考方案13】:

今天最明显的方法是将对象保存为 JSON。

    JSON 可读 JSON 比 XML 更具可读性和更易于使用。 许多允许直接存储 JSON 的非 SQL 数据库。 您的客户端已经使用 JSON 与服务器通信。 (如果没有,很可能是一个错误。)

使用Gson 的示例。

Gson gson = new Gson();
Person[] persons = getArrayOfPersons();
String json = gson.toJson(persons);
System.out.println(json);
//output: ["name":"Tom","age":11,"name":"Jack","age":12]
Person[] personsFromJson = gson.fromJson(json, Person[].class);
//...
class Person 
     public String name;
     public int age;
 

Gson 允许直接转换 List。例子很容易 谷歌搜索。我更喜欢先将列表转换为数组。

【讨论】:

以上是关于如何将对象序列化为字符串的主要内容,如果未能解决你的问题,请参考以下文章

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

如何将对象的json数组反序列化为字典[重复]

如何将动态对象序列化为 dotnet core 中的 JSON 字符串?

如何使用 Json.NET 将 XML 序列化为 JSON 对象

如何将字符串反序列化为对象,然后在颤振中设置为泛型

杰克逊将对象序列化为字符串