161

I am able to serialize an object into a file and then restore it again as is shown in the next code snippet. I would like to serialize the object into a string and store into a database instead. Can anyone help me?

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();
casperOne
  • 73,706
  • 19
  • 184
  • 253
Sergio del Amo
  • 76,835
  • 68
  • 152
  • 179

13 Answers13

290

Sergio:

You should use BLOB. It is pretty straighforward with JDBC.

The problem with the second code you posted is the encoding. You should additionally encode the bytes to make sure none of them fails.

If you still want to write it down into a String you can encode the bytes using java.util.Base64.

Still you should use CLOB as data type because you don't know how long the serialized data is going to be.

Here is a sample of how to use it.

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

Output:

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

NOTE: for Java 7 and earlier you can see the original answer here

Community
  • 1
  • 1
OscarRyz
  • 196,001
  • 113
  • 385
  • 569
  • +1 if you REALLY need strings, then base64+clob is the way to go. – John Gardner Sep 25 '08 at 19:02
  • 6
    +1, Small improvement. Better to use interface Serializable instead of plain Object in toString() method: private static String toString(Serializable object) – tefozi Nov 03 '09 at 05:25
  • 4
    If we try to store the object as an byte array instead of string, then we can achieve the samething without using BASE64. – Sudar Jan 12 '10 at 06:54
  • Does the Base64Coder library even work? I just tried it and it can't even decode its own encodings. I keep getting a java.lang.IllegalArgumentException: Length of Base64 encoded input string is not a multiple of 4. – phreakhead May 31 '13 at 02:29
  • 3
    The fatal flaw in this is that class definitions tend to change over time - if such a change occurs you will be unable to deserialize! Adding a `serialVersionUID` to `SomeClass` will protect against new fields being added but if fields are removed you'll be screwed. It's worth reading what Joshua Bloch has to say on this in Effective Java - http://books.google.co.uk/books?id=ka2VUBqHiWkC&lpg=PP1&dq=effective+java&pg=PA289 – Nick Holt Jul 18 '13 at 08:55
  • @phreakhead Try using Android's Base64 encoder instead. During save and restore, it comes in handy to serialize/deserialize classes to strings. – Neil Oct 12 '13 at 19:46
  • @AlbertHendriks sorry about that, you can see the previous version here: http://stackoverflow.com/revisions/134918/9 – OscarRyz Sep 29 '15 at 15:01
12

How about writing the data to a ByteArrayOutputStream instead of a FileOutputStream?

Otherwise, you could serialize the object using XMLEncoder, persist the XML, then deserialize via XMLDecoder.

Tim Frey
  • 9,901
  • 9
  • 44
  • 60
8

Thanks for great and quick replies. I will gives some up votes inmediately to acknowledge your help. I have coded the best solution in my opinion based on your answers.

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

Note i did not considered using JSON because is less efficient.

Note: I will considered your advice about not storing serialized object as strings in the database but byte[] instead.

Sergio del Amo
  • 76,835
  • 68
  • 152
  • 179
  • 3
    "ByteArrayOutputStream.toString converts using the *platform default encoding*. Are you sure you want that? Particularly as an arbitrary byte array is not valid UTF8. Further, the database is going to mangle it." – Tom Hawtin - tackline Sep 25 '08 at 17:57
  • You should take the above comment from Tom Hawtin seriously – anjanb Sep 25 '08 at 18:12
  • Not to mention long-term storage of serialized objects is not a great idea and not recommended – Steve g Sep 25 '08 at 18:13
  • "Note i did not considered using JSON because is less efficient." How about using google's protocol buffers for efficiency ? Also, Steve g's idea makes perfect sense. One way to store serialized data in a DB yet make it available for languages other than java is again, protocol buffers. – anjanb Sep 25 '08 at 18:50
5

Java8 approach, converting Object from/to String, inspired by answer from OscarRyz. For de-/encoding, java.util.Base64 is required and used.

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();
    }
  }
}
Markus Schulte
  • 4,171
  • 3
  • 47
  • 58
4

How about persisting the object as a blob

Kristian
  • 6,443
  • 6
  • 27
  • 29
4

XStream provides a simple utility for serializing/deserializing to/from XML, and it's very quick. Storing XML CLOBs rather than binary BLOBS is going to be less fragile, not to mention more readable.

skaffman
  • 398,947
  • 96
  • 818
  • 769
3

If you're storing an object as binary data in the database, then you really should use a BLOB datatype. The database is able to store it more efficiently, and you don't have to worry about encodings and the like. JDBC provides methods for creating and retrieving blobs in terms of streams. Use Java 6 if you can, it made some additions to the JDBC API that make dealing with blobs a whole lot easier.

If you absolutely need to store the data as a String, I would recommend XStream for XML-based storage (much easier than XMLEncoder), but alternative object representations might be just as useful (e.g. JSON). Your approach depends on why you actually need to store the object in this way.

Daniel Spiewak
  • 54,515
  • 14
  • 108
  • 120
2

Take a look at the java.sql.PreparedStatement class, specifically the function

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

Then take a look at the java.sql.ResultSet class, specifically the function

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

Keep in mind that if you are serializing an object into a database, and then you change the object in your code in a new version, the deserialization process can easily fail because your object's signature changed. I once made this mistake with storing a custom Preferences serialized and then making a change to the Preferences definition. Suddenly I couldn't read any of the previously serialized information.

You might be better off writing clunky per property columns in a table and composing and decomposing the object in this manner instead, to avoid this issue with object versions and deserialization. Or writing the properties into a hashmap of some sort, like a java.util.Properties object, and then serializing the properties object which is extremely unlikely to change.

Josh
  • 17,834
  • 7
  • 50
  • 68
1

The serialised stream is just a sequence of bytes (octets). So the question is how to convert a sequence of bytes to a String, and back again. Further it needs to use a limited set of character codes if it is going to be stored in a database.

The obvious solution to the problem is to change the field to a binary LOB. If you want to stick with a characer LOB, then you'll need to encode in some scheme such as base64, hex or uu.

Tom Hawtin - tackline
  • 145,806
  • 30
  • 211
  • 305
1

You can use the build in classes sun.misc.Base64Decoder and sun.misc.Base64Encoder to convert the binary data of the serialize to a string. You das not need additional classes because it are build in.

1

Simple Solution,worked for me

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
  • 465
  • 1
  • 4
  • 20
1

Today the most obvious approach is to save the object(s) to JSON.

  1. JSON is readable
  2. JSON is more readable and easier to work with than XML.
  3. A lot of Non-SQL databases that allow storing JSON directly.
  4. Your client already communicates with the server using JSON. (If it doesn't, it is very likely a mistake.)

Example using Gson.

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 allows converting List directly. Examples can be easily googled. I prefer to convert lists to arrays first.

Yuriy N.
  • 4,936
  • 2
  • 38
  • 31
0

you can use UUEncoding

CiNN
  • 9,752
  • 6
  • 44
  • 57