Як серіалізувати об’єкт у рядок


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

Відповіді:


270

Серхіо:

Вам слід використовувати BLOB . Це досить прямо з JDBC.

Проблема з другим опублікованим вами кодом - кодування. Вам слід додатково закодувати байти, щоб переконатися, що жоден з них не виходить з ладу.

Якщо ви все ще хочете записати його в String, ви можете кодувати байти за допомогою 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 - це шлях.
Джон Гарднер

6
+1, Невелике покращення. Краще використовувати інтерфейс Serializable замість простого методу Object in toString (): private
statični

4
Якщо ми спробуємо зберегти об'єкт як байтовий масив замість рядка, то можемо досягти вибірки без використання BASE64.
Судар

2
Фатальна вада в цьому полягає в тому, що визначення класів мають тенденцію змінюватися з часом - якщо така зміна відбудеться, ви не зможете десяріалізувати! Додавання serialVersionUIDTo SomeClassзахищатиме від нових родовищ, доданим , але якщо поля видалені ви будете болти. Варто ознайомитись з тим, що Джошуа Блох має на це сказати в Ефективній Java - books.google.co.uk/…
Нік Холт

1
З Java 8 зараз існує java.util.Base64. Ви повинні оновити свою відповідь: Base64.getEncoder (). EncodeToString (baos.toByteArray ()); Декодування (). Base64.getDecoder (). Декодування;
drUniversalis

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, оскільки це менш ефективно.

Примітка. Я розгляну вашу пораду щодо того, щоб не зберігати серіалізований об'єкт як рядки в базі даних, а байт [].


3
"ByteArrayOutputStream.toString перетворює за допомогою кодування за замовчуванням платформи . Ви впевнені, що цього хочете? Зокрема, як довільний масив байтів не є дійсним UTF8. Крім того, база даних збирається обробляти його."
Том Хотін - тайклін

Ви повинні сприймати вищезгаданий коментар від Тома Хотіна серйозно
anjanb

Не кажучи вже про тривале зберігання серіалізованих об’єктів - це не чудова ідея і не рекомендується
Steve g

"Зауважте, я не розглядав можливість використання JSON, оскільки це менш ефективно." Як щодо ефективності використання буферів протоколів google? Також ідея Стіва g має ідеальний сенс. Одним із способів зберігання серіалізованих даних у БД, однак зробити їх доступними для інших мов, крім Java, є буфери протоколів.
anjanb

5

Підхід Java8, перетворення Об'єкта з / у 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

@ simonalexander2005 Змістовна примітка, я більше не використовував тут інтерфейс. Я це змінив.
Маркус Шульте

4

XStream забезпечує просту утиліту для серіалізації / десеріалізації в / з XML, і це дуже швидко. Зберігання XML CLOB, а не двійкових BLOBS, стане менш крихким, не кажучи вже про більш читабельному.



3

Якщо ви зберігаєте об'єкт як бінарні дані в базі даних, то вам дійсно слід використовувати BLOBтип даних. База даних здатна зберігати її більш ефективно, і вам не доведеться турбуватися про кодування тощо. JDBC надає методи для створення та вилучення крапок з точки зору потоків. Якщо ви можете використовувати Java 6, це зробило деякі доповнення до API JDBC, які значно полегшують справу з краплями.

Якщо вам абсолютно потрібно зберігати дані як 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 і знову. Далі йому потрібно використовувати обмежений набір символьних кодів, якщо він буде зберігатися в базі даних.

Очевидним рішенням проблеми є зміна поля на бінарний 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();
}

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.