Bir nesneyi bir dizeye nasıl serileştirilir


153

Bir nesneyi bir dosyaya serileştirebilir ve ardından sonraki kod parçacığında gösterildiği gibi yeniden geri yükleyebilirim. Nesneyi bir dizge halinde serileştirmek ve bunun yerine bir veritabanında saklamak istiyorum. Biri bana yardım edebilir mi?

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

Yanıtlar:


274

Sergio:

BLOB kullanmalısınız . JDBC ile oldukça doğrudur.

Gönderdiğiniz ikinci kodla ilgili sorun kodlamadır. Hiçbirinin başarısız olmadığından emin olmak için ayrıca baytları kodlamalısınız.

Hala bir String'e yazmak istiyorsanız, baytları java.util.Base64 kullanarak kodlayabilirsiniz .

Yine de veri türü olarak CLOB kullanmalısınız çünkü serileştirilmiş verilerin ne kadar süreceğini bilmiyorsunuz.

İşte nasıl kullanılacağına dair bir örnek.

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

Çıktı:

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

NOT : Java 7 ve öncesi için orijinal yanıtı burada görebilirsiniz


+1, GERÇEKTEN dizelere ihtiyacınız varsa, base64 + clob gitmenin yoludur.
John Gardner

6
+1, Küçük iyileştirme.
ToString

4
Nesneyi dizge yerine bir bayt dizisi olarak saklamaya çalışırsak, aynı şeyi BASE64 kullanmadan elde edebiliriz.
Sudar

2
Buradaki ölümcül kusur, sınıf tanımlarının zaman içinde değişme eğiliminde olmasıdır - eğer böyle bir değişiklik olursa seriyi kaldıramazsınız! Bir serialVersionUIDto eklemek SomeClass, eklenen yeni alanlara karşı koruma sağlar, ancak alanlar kaldırılırsa, mahvolursunuz. Joshua Bloch'un Etkili Java'da bu konuda söylediklerini okumaya değer - books.google.co.uk/…
Nick Holt

1
Java 8'den beri şimdi java.util.Base64 var. Cevabınızı güncellemelisiniz: Base64.getEncoder (). EncodeToString (baos.toByteArray ()); Base64.getDecoder (). Decode (s);
drUniversalis

12

Verileri FileOutputStream yerine bir ByteArrayOutputStream'e yazmaya ne dersiniz?

Aksi takdirde, nesneyi XMLEncoder kullanarak serileştirebilir, XML'i sürdürebilir ve ardından XMLDecoder aracılığıyla seriyi kaldırabilirsiniz.


8

Harika ve hızlı yanıtlar için teşekkürler. Yardımınızı kabul etmek için hemen bazı oyları vereceğim. Cevaplarınıza göre bence en iyi çözümü kodladım.

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

Not JSON kullanmayı düşünmedim çünkü daha az verimli.

Not: Serileştirilmiş nesneyi veritabanında dizeler olarak değil, bunun yerine bayt [] olarak depolamakla ilgili tavsiyenizi dikkate alacağım.


3
"ByteArrayOutputStream.toString, platform varsayılan kodlamasını kullanarak dönüştürür . Bunu istediğinizden emin misiniz? Özellikle keyfi bir bayt dizisi UTF8 olmadığı için. Ayrıca, veritabanı onu karıştıracak."
Tom Hawtin - tackline

Tom
Hawtin'den

Serileştirilmiş nesnelerin uzun süreli saklanmasından bahsetmiyorum bile harika bir fikir değil ve tavsiye edilmiyor
Steve g

"Daha az verimli olduğu için JSON kullanmayı düşünmediğime dikkat edin." Verimlilik için Google'ın protokol arabelleklerini kullanmaya ne dersiniz? Ayrıca Steve g'nin fikri çok mantıklı. Serileştirilmiş verileri bir DB'de depolamanın ancak java dışındaki diller için kullanılabilir hale getirmenin bir yolu yine protokol arabellekleridir.
anjanb

5

Dan cevap esinlenerek Java8 yaklaşım, String / den dönüştürme Nesne, OscarRyz . Kod çözme / kodlama için java.util.Base64 gereklidir ve kullanılır.

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

Bu neden bir sınıftan ziyade bir arayüz?
simonalexander2005

@ simonalexander2005 Anlamlı not, artık burada bir arayüz kullanmayacağım. Ben değiştirdim.
Markus Schulte

4

XStream, XML'e / XML'den serileştirme / seriyi kaldırma için basit bir yardımcı program sağlar ve çok hızlıdır. İkili BLOBS yerine XML CLOB'ları depolamak, daha okunaklı olduğundan bahsetmiyorum bile daha az kırılgan olacak.


4

Nesneyi bir blob olarak sürdürmeye ne dersiniz?


Bağlantıyı düzelt, tamam mı?

3

Bir nesneyi veritabanında ikili veri olarak saklıyorsanız, gerçekten bir BLOBveri türü kullanmalısınız . Veritabanı onu daha verimli bir şekilde saklayabilir ve kodlamalar ve benzerleri hakkında endişelenmenize gerek yoktur. JDBC, akışlar açısından blobların oluşturulması ve alınması için yöntemler sağlar. Mümkünse Java 6'yı kullanın, JDBC API'sine bloblarla başa çıkmayı çok daha kolay hale getiren bazı eklemeler yaptı.

Verileri kesinlikle bir String olarak saklamanız gerekiyorsa, XML tabanlı depolama için XStream'i tavsiye ederim (bundan çok daha kolay XMLEncoder), ancak alternatif nesne gösterimleri de aynı derecede yararlı olabilir (örneğin JSON). Yaklaşımınız, nesneyi neden bu şekilde depolamanız gerektiğine bağlıdır.


2

Java.sql.PreparedStatement sınıfına, özellikle de işleve bir göz atın

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

Ardından java.sql.ResultSet sınıfına, özellikle de işleve bir göz atın.

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

Bir nesneyi bir veritabanına serileştiriyorsanız ve ardından kodunuzdaki nesneyi yeni bir sürümde değiştirirseniz, nesnenizin imzası değiştiği için seriyi kaldırma işleminin kolayca başarısız olabileceğini unutmayın. Bir keresinde bu hatayı özel bir Tercihleri ​​serileştirerek ve ardından Tercihler tanımında bir değişiklik yaparak yaptım. Birden, daha önce serileştirilmiş bilgilerin hiçbirini okuyamadım.

Nesne sürümleri ve seriyi kaldırma ile ilgili bu sorunu önlemek için, bir tabloda özellik başına hantal sütun yazmak ve bunun yerine nesneyi bu şekilde oluşturmak ve ayrıştırmak daha iyi olabilir. Veya özellikleri java.util.Properties nesnesi gibi bir tür karma haritaya yazmak ve ardından değişmesi son derece düşük olan özellikler nesnesini serileştirmek.


1

Serileştirilmiş akış yalnızca bir bayt (sekizli) dizisidir. Öyleyse soru, bir bayt dizisini bir String'e ve tekrar nasıl geri dönüştürebileceğidir. Ayrıca, bir veri tabanında saklanacaksa, sınırlı sayıda karakter kodu kullanması gerekir.

Sorunun açık çözümü, alanı ikili LOB olarak değiştirmektir. Bir karakter LOB ile bağlı kalmak istiyorsanız, base64, hex veya uu gibi bazı şemalarda kodlamanız gerekir.


1

Sun.misc.Base64Decoder ve sun.misc.Base64Encoder sınıflarındaki derlemeyi, serileştirmenin ikili verilerini dizeye dönüştürmek için kullanabilirsiniz. Yerleşik olduğu için ek sınıflara ihtiyacınız yoktur.


1

Basit Çözüm, benim için çalıştı

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


0

Bugün en bariz yaklaşım, nesneleri JSON'a kaydetmektir.

  1. JSON okunabilir
  2. JSON, XML'den daha okunaklı ve kullanımı daha kolaydır.
  3. JSON'u doğrudan depolamaya izin veren birçok SQL olmayan veritabanı.
  4. İstemciniz, JSON kullanarak sunucuyla zaten iletişim kuruyor. (Olmazsa, büyük olasılıkla bir hatadır.)

Gson kullanarak örnek .

Gson gson = new Gson();
Person[] persons = getArrayOfPersons();
String json = gson.toJson(persons);
System.out.println(json);
//output: [{"name":"Tom","age":11},{"name":"Jack","age":12}]
Person[] personsFromJson = gson.fromJson(json, Person[].class);
//...
class Person {
     public String name;
     public int age;
} 

Gson, List'i doğrudan dönüştürmeye izin verir. Örnekler kolayca Google'da incelenebilir. Önce listeleri dizilere dönüştürmeyi tercih ederim.


Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.