Как сериализовать объект в строку

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

Sergio:

Вы должны использовать 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 и более ранних версий вы можете увидеть оригинальный ответ здесь

OscarRyz
источник
+1, если вам действительно нужны строки, тогда base64 + clob - это путь.
Джон Гарднер
6
+1, небольшое улучшение. Лучше использовать интерфейс Serializable вместо простого Object в методе toString (): private static String toString (Serializable object)
tefozi
4
Если мы попытаемся сохранить объект в виде байтового массива вместо строки, то мы сможем достичь того же самого, не используя BASE64.
Судар
2
Фатальный недостаток в том, что определения классов имеют тенденцию меняться со временем - если такое изменение произойдет, вы не сможете десериализоваться! Добавление serialVersionUIDto SomeClassзащитит от добавления новых полей, но если поля будут удалены, вы будете испорчены. Это стоит прочитать , что Джошуа Блох должен сказать об этом в Effective 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.

Outlaw Programmer
источник
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 преобразует, используя кодировку платформы по умолчанию . Вы уверены, что хотите это?
Том Хотин - tackline
Вы должны серьезно отнестись к вышеприведенному комментарию Тома
Хоутина
Не говоря уже о долговременном хранении сериализованных объектов, это не лучшая идея и не рекомендуется
Steve g
«Заметьте, я не рассматривал использование JSON, потому что он менее эффективен». Как насчет использования буферов протокола Google для эффективности? Кроме того, идея Стива Г имеет смысл. Один из способов сохранить сериализованные данные в БД и сделать их доступными для языков, отличных от java, - это снова буферы протокола.
anjanb
5

Подход 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
@ simonalexander2005 Значительное примечание, я бы больше не использовал здесь интерфейс. Я изменил это.
Маркус Шульте
4

XStream предоставляет простую утилиту для сериализации / десериализации в / из XML, и это очень быстро. Хранение XML CLOB, а не двоичных BLOBS будет менее хрупким, не говоря уже о более удобочитаемом.

skaffman
источник
4

Как насчет сохранения объекта в виде BLOB- объекта

Kristian
источник
Исправить ссылку, хорошо?
3

Если вы храните объект в виде двоичных данных в базе данных, вам действительно следует использовать BLOBтип данных. База данных может хранить ее более эффективно, и вам не нужно беспокоиться о кодировках и тому подобном. JDBC предоставляет методы для создания и извлечения BLOB-объектов с точки зрения потоков. Если возможно, используйте Java 6, он сделал некоторые дополнения к API JDBC, которые значительно облегчают работу с каплями.

Если вам абсолютно необходимо хранить данные в виде строки, я бы порекомендовал 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

Сериализованный поток - это просто последовательность байтов (октетов). Поэтому вопрос заключается в том, как преобразовать последовательность байтов в строку и обратно. Кроме того, он должен использовать ограниченный набор кодов символов, если он будет храниться в базе данных.

Очевидное решение проблемы - изменить поле на двоичный LOB. Если вы хотите придерживаться LOB Characer, то вам нужно кодировать в какой-то схеме, например base64, hex или uu.

Том Хотин - Tackline
источник
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();
}
priyanka_rao
источник