Best Practices for Serializing Objects to a Custom String Format for Use in an Output File

Best practices for serializing objects to a custom string format for use in an output file

Here is a generic fashion for creating CSV from a list of objects, using reflection:

public static string ToCsv<T>(string separator, IEnumerable<T> objectlist)
{
Type t = typeof(T);
FieldInfo[] fields = t.GetFields();

string header = String.Join(separator, fields.Select(f => f.Name).ToArray());

StringBuilder csvdata = new StringBuilder();
csvdata.AppendLine(header);

foreach (var o in objectlist)
csvdata.AppendLine(ToCsvFields(separator, fields, o));

return csvdata.ToString();
}

public static string ToCsvFields(string separator, FieldInfo[] fields, object o)
{
StringBuilder linie = new StringBuilder();

foreach (var f in fields)
{
if (linie.Length > 0)
linie.Append(separator);

var x = f.GetValue(o);

if (x != null)
linie.Append(x.ToString());
}

return linie.ToString();
}

Many variations can be made, such as writing out directly to a file in ToCsv(), or replacing the StringBuilder with an IEnumerable and yield statements.

Best method to write objects to a text file

JAXB will be great choice for you, as I see from your problem description. Here is a simple example to start with.

JAXB is a part of standard JDK since 1.6, so you don't need any additional libraries.
Also is supports collections serialization so you can easily implement your "append" task.

How to serialize an object into a string

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

Creating own string format expressions, for custom user generated output

If your users are smart enough, you can just give them instructions for a format string:

Path format string: [                    ]
{0} is replaced with your name, {1} is replaced with date, and takes an optional format after a colon
Example:
{0}\{1:yyyy}\{1:MM} - John\2021\06

And whatever they enter, do a:

var path = string.Format(USER_INPUT, model.Name, model.Date)

If you want to help them out a bit:

Path format string: [                    ]
{name} is replaced with your name, {date} is replaced with date, and takes an optional format after a colon. Fields can be specified multiple times.
Example:
{name}\{date:yyyy}\{date:MM} - John\2021\06

And perform the replacements before you format:

var path = string.Format(USER_INPUT.Replace("{name", "{0").Replace("{date","{1"), model.Name, model.Date)

The same technique can be extended if you want to simplify further:

Path format string: [                    ]
{name} is replaced with your name,
{yyyy} is replaced with the 4 digit year
{MM} is replaced with...

Example:
{name}\{yyyy}\{MM} - John\2021\06

And perform more replacements before you format:

var path = string.Format(USER_INPUT.Replace("{name", "{0").Replace("{yyyy","{1:yyyy").Replace("{MM","{1:MM"), model.Name, model.Date)

Making a file format extensible

If you need a binary format, look at Protocol Buffers, which Google uses internally for RPCs as well as long-term serialization of records. Each field of a protocol buffer is identified by an integer ID. Old applications ignore (and pass through) the fields that they don't understand, so you can safely add new fields. You never reuse deprecated field IDs or change the type of a field.

Protocol buffers support primitive types (bool, int32, int64, string, byte arrays) as well as repeated and even recursively nested messages. Unfortunately they don't support maps, so you have to turn a map into a list of (key, value).

Don't spend all your time fretting about serialization and deserialization. It's not as fun as designing protobufs.

How to write and read java serialized objects into a file

Why not serialize the whole list at once?

FileOutputStream fout = new FileOutputStream("G:\\address.ser");
ObjectOutputStream oos = new ObjectOutputStream(fout);
oos.writeObject(MyClassList);

Assuming, of course, that MyClassList is an ArrayList or LinkedList, or another Serializable collection.

In the case of reading it back, in your code you ready only one item, there is no loop to gather all the item written.

Best way to serialize/unserialize objects in JavaScript?

JSON has no functions as data types. You can only serialize strings, numbers, objects, arrays, and booleans (and null)

You could create your own toJson method, only passing the data that really has to be serialized:

Person.prototype.toJson = function() {
return JSON.stringify({age: this.age});
};

Similar for deserializing:

Person.fromJson = function(json) {
var data = JSON.parse(json); // Parsing the json string.
return new Person(data.age);
};

The usage would be:

var serialize = p1.toJson();
var _p1 = Person.fromJson(serialize);
alert("Is old: " + _p1.isOld());

To reduce the amount of work, you could consider to store all the data that needs to be serialized in a special "data" property for each Person instance. For example:

function Person(age) {
this.data = {
age: age
};
this.isOld = function (){
return this.data.age > 60 ? true : false;
}
}

then serializing and deserializing is merely calling JSON.stringify(this.data) and setting the data of an instance would be instance.data = JSON.parse(json).

This would keep the toJson and fromJson methods simple but you'd have to adjust your other functions.


Side note:

You should add the isOld method to the prototype of the function:

Person.prototype.isOld = function() {}

Otherwise, every instance has it's own instance of that function which also increases memory.



Related Topics



Leave a reply



Submit