Comment sérialiser un objet dans une chaîne


150

Je suis capable de sérialiser un objet dans un fichier, puis de le restaurer à nouveau comme indiqué dans l'extrait de code suivant. Je voudrais sérialiser l'objet dans une chaîne et stocker dans une base de données à la place. Quelqu'un peut-il m'aider?

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

Réponses:


270

Sergio:

Vous devez utiliser BLOB . C'est assez simple avec JDBC.

Le problème avec le deuxième code que vous avez publié est l'encodage. Vous devez également encoder les octets pour vous assurer qu'aucun d'entre eux n'échoue.

Si vous souhaitez toujours l'écrire dans une chaîne, vous pouvez encoder les octets à l'aide de java.util.Base64 .

Néanmoins, vous devez utiliser CLOB comme type de données car vous ne savez pas combien de temps dureront les données sérialisées.

Voici un exemple de son utilisation.

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

Production:

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

REMARQUE : pour Java 7 et versions antérieures, vous pouvez voir la réponse originale ici


+1 si vous avez VRAIMENT besoin de chaînes, alors base64 + clob est la voie à suivre.
John Gardner

6
+1, petite amélioration. Mieux vaut utiliser l'interface Serializable au lieu de plain Object dans la méthode toString (): private static String toString (Serializable object)
tefozi

4
Si nous essayons de stocker l'objet sous forme de tableau d'octets au lieu de chaîne, nous pouvons réaliser le même résultat sans utiliser BASE64.
Sudar

2
Le défaut fatal est que les définitions de classe ont tendance à changer avec le temps - si un tel changement se produit, vous ne pourrez pas désérialiser! L'ajout d'un serialVersionUIDà SomeClassprotégera contre l' ajout de nouveaux champs, mais si les champs sont supprimés, vous serez vissé. Cela vaut la peine de lire ce que Joshua Bloch a à dire à ce sujet dans Effective Java - books.google.co.uk/...
Nick Holt

1
Depuis Java 8, il existe désormais java.util.Base64. Vous devez mettre à jour votre réponse: Base64.getEncoder (). EncodeToString (baos.toByteArray ()); Base64.getDecoder (). Decode (s);
drUniversalis

12

Que diriez-vous d'écrire les données dans un ByteArrayOutputStream au lieu d'un FileOutputStream?

Sinon, vous pouvez sérialiser l'objet à l'aide de XMLEncoder, conserver le XML, puis désérialiser via XMLDecoder.


8

Merci pour les réponses excellentes et rapides. Je renoncerai immédiatement à quelques votes pour reconnaître votre aide. J'ai codé la meilleure solution à mon avis en fonction de vos réponses.

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

Notez que je n'ai pas envisagé d'utiliser JSON car il est moins efficace.

Remarque: Je prendrai en compte votre conseil sur le fait de ne pas stocker d'objet sérialisé sous forme de chaînes dans la base de données, mais plutôt d'octet [].


3
"ByteArrayOutputStream.toString convertit en utilisant le codage par défaut de la plate - forme . Êtes-vous sûr de vouloir cela? En particulier, car un tableau d'octets arbitraire n'est pas UTF8 valide. De plus, la base de données va le déformer."
Tom Hawtin - tackline

Vous devriez prendre le commentaire ci-dessus de Tom Hawtin au sérieux
anjanb

Sans parler du stockage à long terme des objets sérialisés n'est pas une bonne idée et n'est pas recommandé
Steve g

"Notez que je n'ai pas envisagé d'utiliser JSON car il est moins efficace." Que diriez-vous d'utiliser les tampons de protocole de Google pour plus d'efficacité? De plus, l'idée de Steve g est parfaitement logique. Une façon de stocker des données sérialisées dans une base de données tout en les rendant disponibles pour des langues autres que java est encore une fois, les tampons de protocole.
anjanb le

5

Approche Java8, conversion d'objet de / en chaîne, inspirée de la réponse d' OscarRyz . Pour le décodage / encodage, java.util.Base64 est requis et utilisé.

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

Pourquoi est-ce une interface plutôt qu'une classe?
simonalexander2005

@ simonalexander2005 Remarque importante, je n'utiliserais plus d'interface ici. Je l'ai changé.
Markus Schulte

4

XStream fournit un utilitaire simple pour sérialiser / désérialiser vers / depuis XML, et c'est très rapide. Stocker des CLOB XML plutôt que des BLOBS binaires sera moins fragile, pour ne pas dire plus lisible.



3

Si vous stockez un objet sous forme de données binaires dans la base de données, vous devriez vraiment utiliser un BLOBtype de données. La base de données est capable de la stocker plus efficacement et vous n'avez pas à vous soucier des encodages et autres. JDBC fournit des méthodes pour créer et récupérer des objets blob en termes de flux. Utilisez Java 6 si vous le pouvez, cela a apporté quelques ajouts à l'API JDBC qui facilitent grandement la gestion des blobs.

Si vous avez absolument besoin de stocker les données sous forme de chaîne, je recommanderais XStream pour le stockage basé sur XML (beaucoup plus facile que XMLEncoder), mais des représentations d'objet alternatives pourraient être tout aussi utiles (par exemple JSON). Votre approche dépend de la raison pour laquelle vous devez réellement stocker l'objet de cette manière.


2

Jetez un œil à la classe java.sql.PreparedStatement, en particulier la fonction

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

Ensuite, jetez un œil à la classe java.sql.ResultSet, en particulier la fonction

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

Gardez à l'esprit que si vous sérialisez un objet dans une base de données, puis que vous modifiez l'objet dans votre code dans une nouvelle version, le processus de désérialisation peut facilement échouer car la signature de votre objet a changé. Une fois, j'ai fait cette erreur en stockant des préférences personnalisées sérialisées, puis en modifiant la définition des préférences. Soudainement, je ne pouvais lire aucune des informations précédemment sérialisées.

Vous feriez peut-être mieux d'écrire des colonnes par propriété dans une table et de composer et de décomposer l'objet de cette manière à la place, pour éviter ce problème avec les versions d'objet et la désérialisation. Ou écrire les propriétés dans un hashmap d'une sorte, comme un objet java.util.Properties, puis sérialiser l'objet de propriétés qui est extrêmement peu susceptible de changer.


1

Le flux sérialisé est juste une séquence d'octets (octets). La question est donc de savoir comment convertir une séquence d'octets en chaîne, et inversement. En outre, il doit utiliser un ensemble limité de codes de caractères s'il doit être stocké dans une base de données.

La solution évidente au problème est de changer le champ en un LOB binaire. Si vous souhaitez vous en tenir à un LOB de caractères, vous devrez encoder dans un schéma tel que base64, hex ou uu.


1

Vous pouvez utiliser les classes intégrées sun.misc.Base64Decoder et sun.misc.Base64Encoder pour convertir les données binaires de la sérialisation en une chaîne. Vous n avez pas besoin de classes supplémentaires car elles sont intégrées.



0

Solution simple, a fonctionné pour moi

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

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.