วิธีการทำให้เป็นอันดับวัตถุเป็นสตริง


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

เซร์คิโอ:

คุณควรใช้หยด มันค่อนข้างตรงไปตรงมากับ 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, การปรับปรุงเล็กน้อย ดีกว่าที่จะใช้อินเตอร์เฟซ Serializable แทนวัตถุธรรมดาใน toString () วิธีการ: คง String เอกชน toString (วัตถุ Serializable)
tefozi

4
ถ้าเราพยายามที่จะเก็บวัตถุเป็นอาร์เรย์ไบต์แทนที่จะเป็นสตริงเราก็สามารถทำบางสิ่งได้โดยไม่ต้องใช้ BASE64
Sudar

2
ข้อบกพร่องร้ายแรงในที่นี้คือคำจำกัดความของชั้นเรียนมีแนวโน้มที่จะเปลี่ยนแปลงตลอดเวลา - หากการเปลี่ยนแปลงดังกล่าวเกิดขึ้นคุณจะไม่สามารถทำให้เสื่อมเสียชื่อเสียงได้! เพิ่มserialVersionUIDเพื่อSomeClassจะป้องกันเขตข้อมูลใหม่ที่เพิ่มเข้ามา แต่ถ้าเขตข้อมูลจะถูกลบออกคุณจะเมา มันคุ้มค่าที่จะอ่านสิ่งที่ Joshua Bloch พูดในเรื่องนี้ใน Effective Java - books.google.co.th/…
Nick Holt

1
ตั้งแต่ Java 8 ตอนนี้มี java.util.Base64 คุณควรอัปเดตคำตอบของคุณ: Base64.getEncoder (). encodeToString (baos.toByteArray ()); Base64.getDecoder () ถอดรหัส (s).
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 นอกจากนี้ฐานข้อมูลกำลังจะเข้ามาทำลาย"
Tom Hawtin - tackline

คุณควรรับฟังความคิดเห็นข้างต้นจาก Tom Hawtin อย่างจริงจัง
anjanb

ไม่ต้องพูดถึงระยะยาวการจัดเก็บข้อมูลของวัตถุต่อเนื่องไม่ได้เป็นความคิดที่ดีและไม่แนะนำ
สตีฟกรัม

"หมายเหตุฉันไม่ได้พิจารณาการใช้ JSON เพราะมีประสิทธิภาพน้อยกว่า" วิธีการเกี่ยวกับการใช้โปรโตคอลบัฟเฟอร์ของ Google เพื่อประสิทธิภาพ? นอกจากนี้ความคิดของสตีฟจีก็สมเหตุสมผลดี วิธีหนึ่งในการเก็บข้อมูลต่อเนื่องในฐานข้อมูล แต่ทำให้มันพร้อมใช้งานสำหรับภาษาอื่นที่ไม่ใช่จาวาก็คือโปรโตคอลบัฟเฟอร์
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 โน้ตที่มีความหมายฉันจะไม่ใช้อินเทอร์เฟซที่นี่อีกต่อไป ฉันเปลี่ยนมัน
Markus Schulte

4

XStream จัดเตรียมยูทิลิตี้ง่าย ๆ สำหรับการซีเรียลไลซ์เซชั่น / ดีซีเรียลไลซ์ลิ่งถึง / จาก XML และมันรวดเร็วมาก การจัดเก็บ XML CLOBs แทนที่จะเป็น BLOBS แบบไบนารีจะมีความเปราะบางน้อยกว่า



3

หากคุณกำลังจัดเก็บวัตถุเป็นข้อมูลไบนารีในฐานข้อมูลคุณควรใช้BLOBประเภทข้อมูลจริง ๆ ฐานข้อมูลสามารถจัดเก็บได้อย่างมีประสิทธิภาพมากขึ้นและคุณไม่ต้องกังวลเกี่ยวกับการเข้ารหัสและอื่น ๆ JDBC จัดเตรียมวิธีสำหรับการสร้างและดึง Blobs ในแง่ของสตรีม ใช้ Java 6 ถ้าทำได้สามารถเพิ่ม JDBC API ที่ทำให้จัดการกับ blobs ได้ง่ายขึ้น

หากคุณต้องการเก็บข้อมูลเป็น 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)

โปรดทราบว่าหากคุณซีเรียลไลซ์วัตถุเป็นฐานข้อมูลจากนั้นคุณเปลี่ยนวัตถุในรหัสของคุณในเวอร์ชันใหม่กระบวนการดีซีเรียลไลเซชันอาจล้มเหลวได้ง่ายเนื่องจากลายเซ็นของวัตถุของคุณเปลี่ยนไป ฉันเคยทำผิดพลาดนี้ด้วยการจัดเก็บการตั้งค่าที่กำหนดเองต่อเนื่องและทำการเปลี่ยนแปลงคำจำกัดความการตั้งค่า ทันใดนั้นฉันไม่สามารถอ่านข้อมูลที่ต่อเนื่องกันก่อนหน้านี้ได้

คุณควรเขียน clunky ต่อคอลัมน์คุณสมบัติในตารางและเขียนและย่อยสลายวัตถุในลักษณะนี้แทนเพื่อหลีกเลี่ยงปัญหานี้ด้วยรุ่นของวัตถุและการดีซีเรียลไลเซชัน หรือเขียนคุณสมบัติลงใน hashmap ของการเรียงลำดับบางอย่างเช่นวัตถุ java.util.Properties จากนั้นจัดลำดับวัตถุคุณสมบัติซึ่งไม่น่าจะเปลี่ยนแปลงได้อย่างมาก


1

สตรีมต่อเนื่องเป็นเพียงลำดับของไบต์ (octets) ดังนั้นคำถามคือวิธีการแปลงลำดับของไบต์เป็นสตริงและกลับมาอีกครั้ง นอกจากนี้ยังจำเป็นต้องใช้รหัสอักขระที่ จำกัด หากจะถูกเก็บไว้ในฐานข้อมูล

วิธีแก้ไขปัญหาที่ชัดเจนคือเปลี่ยนฟิลด์เป็น LOB แบบไบนารี หากคุณต้องการที่จะยึดติดกับ LOB characer คุณจะต้องเข้ารหัสในรูปแบบบางอย่างเช่น base64, hex หรือ uu


1

คุณสามารถใช้ build ในคลาส 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();
}

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.