객체를 문자열로 직렬화하는 방법


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를 사용하면 매우 간단합니다.

게시 한 두 번째 코드의 문제점은 인코딩입니다. 실패하지 않도록 바이트를 추가로 인코딩해야합니다.

여전히 문자열에 쓰려면 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 인터페이스를 사용하는 것이 좋습니다. private static String toString (Serializable object)
tefozi

4
문자열 대신 바이트 배열로 객체를 저장하려고하면 BASE64를 사용하지 않고도 동일한 결과를 얻을 수 있습니다.
Sudar

2
이것의 치명적인 결함은 클래스 정의가 시간이 지남에 따라 변경되는 경향이 있다는 것입니다. 그러한 변경이 발생하면 직렬화 해제 할 수 없습니다! 추가 serialVersionUID하려면 SomeClass추가되는 새로운 분야에 대해 보호 할 수 있지만 필드가 제거되면 당신은 나사됩니다. 효과적인 자바에서 Joshua Bloch의 말을 읽어 볼 가치가 있습니다 -books.google.co.uk/…
Nick Holt

1
Java 8부터 java.util.Base64가 있습니다. 답을 업데이트해야합니다. Base64.getEncoder (). encodeToString (baos.toByteArray ()); Base64.getDecoder (). decode (s);
drUniversalis

12

FileOutputStream 대신 ByteArrayOutputStream에 데이터를 쓰는 것은 어떻습니까?

그렇지 않으면 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-tackline

Tom Hawtin의 위의 의견을 진지하게 받아 들여야합니다
anjanb

직렬화 된 객체의 장기 저장은 말할 것도없고 좋은 아이디어도 아닙니다.
Steve g

"참고 : 효율성이 떨어지기 때문에 JSON 사용을 고려하지 않았습니다." 효율성을 위해 Google의 프로토콜 버퍼를 사용하는 것은 어떻습니까? 또한 Steve g의 아이디어는 완벽합니다. 직렬화 된 데이터를 DB에 저장하면서 Java 이외의 언어에서 사용할 수있게하는 한 가지 방법은 프로토콜 버퍼입니다.
anjanb

5

에서 대답에서 영감을 Java8 방법, 문자열에 /에서 변환 개체 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 의미는, 더 이상 인터페이스를 사용하지 않을 것입니다. 나는 그것을 바꿨다.
Markus Schulte

4

XStream은 XML과의 직렬화 / 직렬화를위한 간단한 유틸리티를 제공하며 매우 빠릅니다. 이진 BLOBS가 아닌 XML CLOB를 저장하면 더 쉽게 읽을 수있는 것은 말할 것도없고 덜 취약합니다.



3

데이터베이스에 객체를 이진 데이터로 저장하는 경우 실제로 BLOB데이터 유형을 사용해야합니다 . 데이터베이스는 데이터베이스를보다 효율적으로 저장할 수 있으므로 인코딩 등에 대해 걱정할 필요가 없습니다. JDBC는 스트림 측면에서 블롭을 작성하고 검색하는 메소드를 제공합니다. 가능하면 Java 6을 사용하면 JDBC API를 추가하여 얼룩을 훨씬 쉽게 처리 할 수 ​​있습니다.

데이터를 String으로 저장 해야하는 경우 XML 기반 스토리지 용 XStream 을 권장 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)

객체를 데이터베이스로 직렬화 한 다음 코드에서 객체를 새 버전으로 변경하면 객체의 서명이 변경되어 역 직렬화 프로세스가 쉽게 실패 할 수 있습니다. 나는 한 번 사용자 정의 환경 설정을 직렬화 한 다음 환경 설정 정의를 변경 하여이 실수를 저질렀습니다. 갑자기 이전에 직렬화 된 정보를 읽을 수 없었습니다.

개체 버전 및 역 직렬화와 관련된이 문제를 피하기 위해 테이블의 속성 열당 clunky를 작성하는 대신이 방법으로 개체를 구성 및 분해하는 것이 좋습니다. 또는 java.util.Properties 객체와 같은 일종의 해시 맵에 속성을 쓴 다음 변경 될 가능성이 거의없는 속성 객체를 직렬화합니다.


1

직렬화 된 스트림은 일련의 바이트 (옥텟)입니다. 따라서 질문은 일련의 바이트를 문자열로 변환하고 다시 되 돌리는 방법입니다. 또한 데이터베이스에 저장 될 경우 제한된 문자 코드 세트를 사용해야합니다.

이 문제에 대한 확실한 해결책은 필드를 이진 LOB로 변경하는 것입니다. characer LOB를 고수하려면 base64, hex 또는 uu와 같은 일부 체계로 인코딩해야합니다.


1

클래스 sun.misc.Base64Decoder 및 sun.misc.Base64Encoder에서 빌드를 사용하여 직렬화의 이진 데이터를 문자열로 변환 할 수 있습니다. 내장되어 있기 때문에 추가 클래스가 필요하지 않습니다.


0

UUEncoding을 사용할 수 있습니다


0

나를 위해 일한 간단한 솔루션

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

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.