シリアライズ
処理速度の比較 :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]をつけるかどうかに関わらず