シリアライズ

処理速度の比較 :protostuff > fst > kryo > fastjson > jackson > jdk

データサイズの比較 :protostuff < kryo < fst < fastjson < jackson < jdk

Java

シリアライズされないもの

・transientメンバー

・staticメンバー

★自作シリアライズ

public class Login implements Serializable {

private static final long serialVersionUID = 1L;

private String username;

private transient String password; // センシティブなデータを取り除く

public Login(String name, String pwd) {

this.username = name;

this.password = pwd;

}

@Override

public String toString() {

return String.format("%s:%s", this.username, this.password);

}

private void writeObject(ObjectOutputStream stream) throws IOException {

stream.defaultWriteObject();

stream.writeObject(this.password); ←ポイント

stream.writeUTF(name);

byte[] pass = this.password.getBytes("UTF-8");

for(int i = 0; i < pass.length; i++){

pass[i] = (byte)(pass[i] ^ 32); //暗号化

}

stream.write(pass);

}

private void readObject(ObjectInputStream stream)

throws IOException, ClassNotFoundException {

stream.defaultReadObject();

this.password = (String)stream.readObject(); ←ポイント

this.name = stream.readUTF();

byte[] pass = new byte[1024];

int size = stream.read(pass, 0, 1024);

for(int i = 0; i < pass.length; i++){

pass[i] = (byte)(pass[i] ^ 32); //復号

}

String password = new String(pass, 0, size, "UTF-8");

this.password = password;

}

public static void main(String[] args)

throws IOException, ClassNotFoundException {

Login login = new Login("Andy", "1234");

System.out.println(login.toString());

Login login2 = (Login)deepCopy(login);

System.out.println(login2.toString());

}

}

青いコードがない場合

Andy:1234

Andy:null

青いコードがある場合

Andy:1234

Andy:1234

テストコード(共用)

public static Object deepCopy(Object obj){

try {

ByteArrayOutputStream bos = new ByteArrayOutputStream();

ObjectOutputStream oos = new ObjectOutputStream(bos);

//ObjectOutputStream oos =

new ObjectOutputStream(new FileOutputStream("D://login.txt"));

oos.writeObject(obj);

ByteArrayInputStream bis = new ByteArrayInputStream(bos.toByteArray());

ObjectInputStream ois = new ObjectInputStream(bis);

//ObjectInputStream ois =

new ObjectInputStream(new FileInputStream("D://login.txt"));

return ois.readObject();

} catch (IOException e) {

throw new IllegalArgumentException(e);

} catch (ClassNotFoundException e) {

throw new IllegalArgumentException(e);

} finally {

//bos.close();

//oos.close();

//bis.close();

//ois.close();

}

}

★内部クラスのシリアライズ

1.外部クラスのみの場合

public class Outer implements Serializable {

class Inner {

...

}

}

2.外部クラスと内部クラスの両方の場合

public class Outer implements Serializable {

static class Inner implements Serializable {

...

}

}

※staticで宣言する意味:

内部クラスをシリアライズする際に、一緒に外部クラスがシリアライズされてしまうのを防ぐため

★writeReplacとreadResolve用法

class PersonProxy implements Serializable{

private String data;

public PersonProxy(Person person){

this.data = person.name + "," + person.age;

}

private Object readResolve() throws ObjectStreamException{

String splits = data.split(",");

String name = splits[0];

int age = Integer.parseInt(splits[1]);

return new Person(name, age);

}

}

class Person implements Serializable{

private String name;

private int age;

public Person(String name, int age){

this.name = name;

this.age = age;

}

private Object writeReplace() throws ObjectStreamException{

return new PersonProxy(this);

}

}

★Externalizable用法

class Person{

protected String name;

protected int age;

public Person(){}

}

class Employee extends Person implements Externalizable{

private transient double salary;

public Employee(){}

public Employee(String name, int age, double salary){

this.name = name;

this.age = age;

this.salary = salary;

}

@Override

public void readExternal(ObjectInput in)

throws IOException, ClassNotFoundException {

this.name = in.readUTF();

this.age = in.readInt();

this.salary = in.readDouble();

}

@Override

public void writeExternal(ObjectOutput out) throws IOException{

out.writeUTF(this.name);

out.writeInt(this.age);

out.writeDouble(this.salary);

}

}

★Serializable vs Externalizable

public interface Serializable {}

public interface Externalizable extends Serializable {

void readExternal(ObjectInput in);

void writeExternal(ObjectOutput out);

}

・Serializableの場合 自動(JVM)、簡単、性能が悪い

class Dummy implements Serializable {

private String msg;

// setter/getter

// Overrideされないように

private void writeObject(final ObjectOutputStream out) throws IOException {

System.out.println("writeObject");

out.writeObject(this.msg == null ? "dummy" : this.msg);

}

private void readObject(final ObjectInputStream in)

throws IOException, ClassNotFoundException {

System.out.println("readObject");

this.msg = (String) in.readObject();

}

// サブクラスはメソッドを継承できるように

protected Object writeReplace() {

System.out.println("writeReplace");

return this;

}

protected Object readResolve() {

System.out.println("readResolve");

return this;

}

}

deepCopy(new Dummy());

出力:

writeReplace

writeObject

readObject

readResolve

・Externalizableの場合 カスタム、性能が良い

class Dummy implements Externalizable {

private String msg;

// 必要

public Dummy() {}

// setter/getter

// 無視された

private void writeObject(final ObjectOutputStream out) throws IOException {

...

}

// 無視された

private void readObject(final ObjectInputStream in)

throws IOException, ClassNotFoundException {

...

}

protected Object writeReplace() {

System.out.println("writeReplace");

return this;

}

protected Object readResolve() {

System.out.println("readResolve");

return this;

}

@Override

public void writeExternal(ObjectOutput out) throws IOException {

System.out.println("writeExternal");

out.writeObject("test");

out.write(10);

}

@Override

public void readExternal(ObjectInput in)

throws IOException, ClassNotFoundException {

System.out.println("readExternal");

// 書き方1:一般的に

String str = (String) in.readObject();

int num = in.read();

// 書き方2:上書きと競合を防ぐ

synchronized (lock) {

if(!flag) {

String str = (String) in.readObject();

int num = in.read();

...

flag = true;

} else {

throw new IllegalStateException();

}

}

}

private final Object lock = new Object();

private boolean flag = false;

}

deepCopy(new Dummy());

出力:

writeReplace

writeExternal

readExternal

readResolve

★writeObjectメソッドでストリームに配列を渡す注意点

public class Sample implements Serializable() {

protected byte[] field;

private void writeObject(ObjectOutputStream out) throws IOException {

out.write((byte[])field.clone());

}

}

※もしObjectOutputStream#write()がオーバーライドされると、

配列フィールドの内容が書き換えられる可能性があるので

C#

★BinaryFormatter vs XmlSerializer

・BinaryFormatter

バイナリ形式のデータ

効率よく

ある程度にセキュリティ

すべてのメンバーはOK

・XmlSerializer

privateメンバーはNG

読み可能

・DataContractJsonSerializer

JSON文字列

public static class SerializerUtils

{

// Type⇒String

public static string SerializeToString<T>(T t)

{

using (MemoryStream stream = new MemoryStream())

{

BinaryFormatter formatter = new BinaryFormatter();

formatter.Serialize(stream, t);

DataContractJsonSerializer ser

= new DataContractJsonSerializer(typeof(T));

ser.WriteObject(stream, t);

// 書き方1

return System.Text.Encoding.UTF8.GetString(stream.ToArray());

// 書き方2

//stream.Position = 0;

//StreamReader sr = new StreamReader(stream);

//return sr.ReadToEnd();

}

}

// Type⇒File

public static void SerializeToFile<T>(T t, string path, string fileName)

{

if(!Directory.Exists(path))

{

Directory.CreateDirectory(path);

}

string fullPath = string.Format(@"{0}\{1}", path, fileName);

//using (FileStream stream

= new FileStream(fullPath, FileMode.Create, FileAccess.Write))

using (FileStream stream

= new FileStream(fullPath, FileMode.OpenOrCreate))

{

BinaryFormatter formatter = new BinaryFormatter();

formatter.Serialize(stream, t);

stream.Flush();

XmlSerializer xs = new XmlSerializer(typeof(T));

xs.Serialize(stream, t);

stream.Flush();

}

}

// String⇒Type

public static T DeserializeFromString<T>(string s) where T : class

{

byte[] bytes = System.Text.Encoding.UTF8.GetBytes(s);

using (MemoryStream stream = new MemoryStream(bytes))

{

BinaryFormatter formatter = new BinaryFormatter();

return formatter.Deserialize(stream) as T;

DataContractJsonSerializer ser

= new DataContractJsonSerializer(typeof(T));

return ser.ReadObject(stream) as T;

}

}

// File⇒Type

public static T DeserializeFromFile<T>(string path) where T : class

{

//using (FileStream stream

= new FileStream(path, FileMode.Open, FileAccess.Read))

using (FileStream stream = new FileStream(path, FileMode.Open))

{

BinaryFormatter formatter = new BinaryFormatter();

return formatter.Deserialize(stream) as T;

XmlSerializer xs = new XmlSerializer(typeof(T));

return xs.Deserialize(stream) as T;

}

}

}

★NonSerializedについて

[Serializable]

public class Person

{

private string name;

public string Name

{

get { return this.name; }

set

{

if(this.NameChanged != null)

{

this.NameChanged(this, null);

}

this.name = value;

}

}

public int Age { get; set; }

[NonSerialized]

private string office;

public string Office

{

get { return this.office; }

set { this.office = value; }

}

[field:NonSerialized]

public event EventHandler NameChanged;

}

※実際にプロパティはメソッドなので、[NonSerialized]をつけるのは×

★自作シリアライズ

1.Attribute (OnDeserialized, OnDeserializing, OnSerialized, OnSerializing)

[Serializable]

public class Person

{

public string FirstName { get; set; }

public string LastName { get; set; }

[NonSerialized]

private string fullName;

public string FullName // シリアライズ化の必要がない

{

get { return this.fullName; }

set { this.fullName = value; }

}

[OnDeserialized]

void OnSerialized(StreamingContext context)

{

this.FullName = string.Format("{0} {1}", this.LastName, this.FirstName);

}

}

2.ISerializableインタフェース

[Serializable]

public class Person : ISerializable

{

...

public Person() { }

protected Person(SerializationInfo info, StreamingContext context)

{

this.FirstName = info.GetString("FirstName");

this.LastName = info.GetString("LastName");

this.FullName = string.Format("{0} {1}", this.LastName, this.FirstName);

}

void ISerializable.GetObjectData(

SerializationInfo info, StreamingContext context)

{

info.AddValue("FirstName", this.FirstName);

info.AddValue("LastName", this.LastName);

}

}

※[NonSerialized]をつけるかどうかに関わらず