XStream

シリアライゼーションJavaXMLJSONオブジェクトマッピングリフレクション軽量ライブラリ

ライブラリ

XStream

概要

XStreamは、JavaオブジェクトをXMLに相互変換するための軽量で使いやすいライブラリです。リフレクションを使用してオブジェクトグラフを自動的に解析し、マッピング定義なしで多くのオブジェクトをシリアライズできます。XML以外にもJSONフォーマットをサポートし、設定ファイルの処理、Webサービスのデータ交換、オブジェクトの永続化など幅広い用途で利用されています。

詳細

XStream 1.4.20は、10年以上の開発実績を持つ成熟したライブラリです。2025年現在も活発にメンテナンスされており、JavaオブジェクトとXML間の変換において高い信頼性を提供しています。アノテーション不要でほとんどのオブジェクトをシリアライズでき、プライベートフィールドやfinalフィールドも処理可能です。セキュリティ機能も強化されており、デシリアライズ時の型制御により安全な処理を実現しています。

主な特徴

  • リフレクションベース: マッピング定義なしで自動的にオブジェクト構造を解析
  • アノテーション不要: デフォルトでほとんどのオブジェクトをシリアライズ可能
  • 多様な出力形式: XML、JSON、その他のフォーマットをサポート
  • カスタマイズ性: コンバーターによる変換ロジックのカスタマイズ
  • セキュリティ機能: デシリアライズ時の型制御による安全性確保
  • スレッドセーフ: 設定後のXStreamインスタンスはスレッドセーフ

メリット・デメリット

メリット

  • 学習コストが非常に低く、数行のコードで基本的な変換が可能
  • マッピング定義が不要で、既存のJavaクラスをそのまま使用可能
  • プライベートフィールドやfinalフィールドも含めて完全なオブジェクトグラフをシリアライズ
  • XMLが人間に読みやすく、デバッグやテストが容易
  • エイリアス機能により要素名や属性名を柔軟にカスタマイズ可能
  • JUnitフレンドリーで単体テストでの利用に最適

デメリット

  • XMLベースのためバイナリ形式と比較してサイズが大きい
  • パフォーマンスがJacksonなどの最新ライブラリと比較して劣る場合がある
  • リフレクション使用のため難読化との相性が悪い
  • XMLに特化しているため、JSONを主に扱う場合は他のライブラリが推奨される
  • セキュリティ設定を適切に行わないと脆弱性のリスクがある
  • 大規模なオブジェクトグラフでメモリ使用量が増加する可能性

参考ページ

書き方の例

基本的なセットアップ

<dependency>
    <groupId>com.thoughtworks.xstream</groupId>
    <artifactId>xstream</artifactId>
    <version>1.4.20</version>
</dependency>

基本的な使用例

import com.thoughtworks.xstream.XStream;

// XStreamインスタンスの作成
XStream xstream = new XStream();

// セキュリティ設定(必須)
xstream.allowTypesByWildcard(new String[] {
    "com.example.myapp.**"
});

// Personクラスの例
public class Person {
    private String name;
    private int age;
    private Address address;
    
    // コンストラクタ、ゲッター、セッター省略
}

// オブジェクトからXMLへ
Person person = new Person("山田太郎", 30);
String xml = xstream.toXML(person);
System.out.println(xml);

// XMLからオブジェクトへ
Person restored = (Person) xstream.fromXML(xml);

エイリアスの設定

// クラス名のエイリアス
xstream.alias("person", Person.class);
xstream.alias("address", Address.class);

// フィールド名のエイリアス
xstream.aliasField("full-name", Person.class, "name");
xstream.aliasField("years", Person.class, "age");

// 属性として出力
xstream.useAttributeFor(Person.class, "age");
xstream.aliasAttribute(Person.class, "age", "years");

カスタムコンバーターの作成

import com.thoughtworks.xstream.converters.Converter;
import com.thoughtworks.xstream.converters.MarshallingContext;
import com.thoughtworks.xstream.converters.UnmarshallingContext;
import com.thoughtworks.xstream.io.HierarchicalStreamReader;
import com.thoughtworks.xstream.io.HierarchicalStreamWriter;

public class DateConverter implements Converter {
    private SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
    
    public boolean canConvert(Class type) {
        return type.equals(Date.class);
    }
    
    public void marshal(Object source, HierarchicalStreamWriter writer,
                       MarshallingContext context) {
        Date date = (Date) source;
        writer.setValue(formatter.format(date));
    }
    
    public Object unmarshal(HierarchicalStreamReader reader,
                           UnmarshallingContext context) {
        try {
            return formatter.parse(reader.getValue());
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}

// コンバーターの登録
xstream.registerConverter(new DateConverter());

JSON形式での出力

import com.thoughtworks.xstream.io.json.JettisonMappedXmlDriver;
import com.thoughtworks.xstream.io.json.JsonHierarchicalStreamDriver;

// JSON用XStreamの作成
XStream xstream = new XStream(new JsonHierarchicalStreamDriver());

// オブジェクトをJSONに変換
Person person = new Person("田中花子", 25);
String json = xstream.toXML(person);
System.out.println(json);

// JSONからオブジェクトに変換
Person restored = (Person) xstream.fromXML(json);

コレクションの処理

// リストのシリアライズ
List<Person> people = Arrays.asList(
    new Person("山田太郎", 30),
    new Person("田中花子", 25)
);

xstream.alias("people", List.class);
xstream.alias("person", Person.class);
xstream.addImplicitCollection(List.class, "list");

String xml = xstream.toXML(people);

セキュリティ設定の詳細

// セキュリティを重視した設定
XStream xstream = new XStream();

// 許可する型を明示的に指定
xstream.allowTypes(new Class[] {
    Person.class,
    Address.class,
    Date.class
});

// パッケージ単位での許可
xstream.allowTypesByWildcard(new String[] {
    "com.example.myapp.model.**",
    "java.util.**",
    "java.lang.**"
});

// 特定の型を拒否
xstream.denyTypes(new Class[] {
    java.beans.EventHandler.class
});