Làm thế nào để tuần tự hóa một đối tượng thành một chuỗi


149

Tôi có thể tuần tự hóa một đối tượng thành một tệp và sau đó khôi phục lại nó như được hiển thị trong đoạn mã tiếp theo. Tôi muốn tuần tự hóa đối tượng thành một chuỗi và lưu trữ vào cơ sở dữ liệu. Ai giúp tôi với?

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

Câu trả lời:


269

Sergio:

Bạn nên sử dụng BLOB . Nó khá căng thẳng với JDBC.

Vấn đề với mã thứ hai bạn đã đăng là mã hóa. Bạn cũng nên mã hóa các byte để đảm bảo không có lỗi nào trong số chúng bị lỗi.

Nếu bạn vẫn muốn viết nó thành Chuỗi, bạn có thể mã hóa các byte bằng java.util.Base64 .

Tuy nhiên, bạn vẫn nên sử dụng CLOB làm kiểu dữ liệu vì bạn không biết dữ liệu được xê-ri hóa sẽ kéo dài bao lâu.

Dưới đây là một mẫu về cách sử dụng nó.

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;
    }
}

Đầu ra:

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

LƯU Ý : đối với Java 7 trở về trước, bạn có thể xem câu trả lời ban đầu tại đây


+1 nếu bạn THỰC SỰ cần chuỗi, thì Base64 + clob là cách để đi.
John Gardner

6
+1, Cải thiện nhỏ. Tốt hơn nên sử dụng giao diện Nối tiếp thay vì đối tượng đơn giản trong phương thức toString (): chuỗi tĩnh riêng toString (đối tượng nối tiếp)
tefozi

4
Nếu chúng ta cố gắng lưu trữ đối tượng dưới dạng một mảng byte thay vì chuỗi, thì chúng ta có thể đạt được việc lấy mẫu mà không cần sử dụng BASE64.
Sudar

2
Lỗ hổng nghiêm trọng ở đây là các định nghĩa lớp có xu hướng thay đổi theo thời gian - nếu thay đổi như vậy xảy ra, bạn sẽ không thể giải trừ! Thêm một serialVersionUIDvào SomeClasssẽ bảo vệ chống lại các trường mới được thêm vào nhưng nếu các trường bị xóa, bạn sẽ bị vặn. Thật đáng để đọc những gì Joshua Bloch đã nói về điều này trong Java hiệu quả - Books.google.co.uk/ Kẻ
Nick Holt

1
Kể từ Java 8, giờ đã có java.util.Base64. Bạn nên cập nhật câu trả lời của mình: Base64.getEncoder (). EncodeToString (baos.toByteArray ()); Base64.getDecoder (). Giải mã (s);
drUniversalis 15/05/2015

12

Làm thế nào về việc ghi dữ liệu vào ByteArrayOutputStream thay vì FileOutputStream?

Mặt khác, bạn có thể tuần tự hóa đối tượng bằng XMLEncoder, duy trì XML, sau đó giải tuần tự hóa thông qua XMLDecoder.


8

Cảm ơn đã trả lời tuyệt vời và nhanh chóng. Tôi sẽ đưa ra một số phiếu bầu ngay lập tức để ghi nhận sự giúp đỡ của bạn. Tôi đã mã hóa giải pháp tốt nhất theo ý kiến ​​của tôi dựa trên câu trả lời của bạn.

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

Lưu ý tôi đã không cân nhắc sử dụng JSON vì kém hiệu quả hơn.

Lưu ý: Tôi sẽ xem xét lời khuyên của bạn về việc không lưu trữ đối tượng nối tiếp dưới dạng chuỗi trong cơ sở dữ liệu mà thay vào đó là byte [].


3
"ByteArrayOutputStream.toString chuyển đổi bằng cách sử dụng mã hóa mặc định của nền tảng . Bạn có chắc chắn muốn điều đó không? Đặc biệt là một mảng byte tùy ý không hợp lệ UTF8. Hơn nữa, cơ sở dữ liệu sẽ xử lý nó."
Tom Hawtin - tackline

Bạn nên nghiêm túc nhận xét trên từ Tom Hawtin
anjanb

Chưa kể việc lưu trữ lâu dài các đối tượng được
Steve g

"Lưu ý tôi đã không cân nhắc sử dụng JSON vì kém hiệu quả hơn." Làm thế nào về việc sử dụng bộ đệm giao thức của google cho hiệu quả? Ngoài ra, ý tưởng của Steve g có ý nghĩa hoàn hảo. Một cách để lưu trữ dữ liệu tuần tự trong DB nhưng làm cho nó có sẵn cho các ngôn ngữ khác ngoài java là bộ đệm giao thức.
anjanb

4

XStream cung cấp một tiện ích đơn giản để tuần tự hóa / giải tuần tự hóa đến / từ XML và nó rất nhanh chóng. Lưu trữ các CLOB XML thay vì BLOBS nhị phân sẽ ít dễ vỡ hơn, chưa kể dễ đọc hơn.



4

Cách tiếp cận Java8, chuyển đổi Object từ / sang String, lấy cảm hứng từ câu trả lời từ OscarRyz . Để khử / mã hóa, java.util.Base64 được yêu cầu và sử dụng.

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

Tại sao đây là một giao diện chứ không phải là một lớp?
simonalexander2005

@ simonalexander2005 Lưu ý có ý nghĩa, tôi sẽ không sử dụng giao diện ở đây nữa. Tôi đã thay đổi nó.
Markus Schulte

3

Nếu bạn đang lưu trữ một đối tượng dưới dạng dữ liệu nhị phân trong cơ sở dữ liệu, thì bạn thực sự nên sử dụng một BLOBkiểu dữ liệu. Cơ sở dữ liệu có thể lưu trữ nó hiệu quả hơn và bạn không phải lo lắng về mã hóa và những thứ tương tự. JDBC cung cấp các phương thức để tạo và truy xuất các đốm màu theo các luồng. Sử dụng Java 6 nếu bạn có thể, nó đã thực hiện một số bổ sung cho API JDBC giúp việc xử lý các đốm dễ dàng hơn rất nhiều.

Nếu bạn thực sự cần lưu trữ dữ liệu dưới dạng Chuỗi, tôi sẽ khuyên dùng XStream cho lưu trữ dựa trên XML (dễ dàng hơn nhiều XMLEncoder), nhưng các biểu diễn đối tượng thay thế có thể cũng hữu ích (ví dụ JSON). Cách tiếp cận của bạn phụ thuộc vào lý do tại sao bạn thực sự cần lưu trữ đối tượng theo cách này.


2

Hãy xem lớp java.sql.PreparedStatement, cụ thể là hàm

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

Sau đó hãy xem lớp java.sql.ResultSet, cụ thể là hàm

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

Hãy nhớ rằng nếu bạn đang tuần tự hóa một đối tượng vào cơ sở dữ liệu, và sau đó bạn thay đổi đối tượng trong mã của mình trong một phiên bản mới, quá trình khử lưu huỳnh có thể dễ dàng thất bại vì chữ ký của đối tượng của bạn đã thay đổi. Tôi đã từng mắc lỗi này khi lưu trữ Tùy chọn tùy chỉnh được tuần tự hóa và sau đó thực hiện thay đổi đối với định nghĩa Tùy chọn. Đột nhiên tôi không thể đọc bất kỳ thông tin nào được đăng tuần trước.

Bạn có thể tốt hơn hết là viết clunky cho mỗi cột thuộc tính trong một bảng và thay vào đó, soạn thảo và phân tách đối tượng theo cách này, để tránh vấn đề này với các phiên bản đối tượng và khử lưu huỳnh. Hoặc viết các thuộc tính vào một hashmap của một số loại, như một đối tượng java.util.ProperIES, sau đó tuần tự hóa các đối tượng thuộc tính rất khó thay đổi.


1

Luồng được tuần tự hóa chỉ là một chuỗi các byte (octet). Vì vậy, câu hỏi là làm thế nào để chuyển đổi một chuỗi byte thành Chuỗi và quay lại. Hơn nữa, nó cần sử dụng một bộ mã ký tự giới hạn nếu nó sẽ được lưu trữ trong cơ sở dữ liệu.

Giải pháp rõ ràng cho vấn đề là thay đổi trường thành LOB nhị phân. Nếu bạn muốn gắn bó với LOB characer, thì bạn sẽ cần mã hóa trong một số lược đồ như base64, hex hoặc uu.


1

Bạn có thể sử dụng bản dựng trong các lớp sun.misc.Base64Decoder và sun.misc.Base64Encoder để chuyển đổi dữ liệu nhị phân của serialization thành một chuỗi. Bạn không cần các lớp bổ sung vì nó được tích hợp sẵn.



0

Giải pháp đơn giản, làm việc cho tôi

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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.