- simple json encoder/decoder for java
SourceForge.jp
 

JSONICとは

JSONICは、Java用のシンプルかつ高機能なJSONエンコーダー/デコーダーライブラリです。
Java用のJSONライブラリはすでに多数存在しますが、JSONICはRFC 4627に従った正式なJSON形式でのデコード/エンコードを行いながらも、プログラミング言語に依存する情報をJSON内に含めることなくPOJO(Plain Old Java Object)と自然な変換を行える点に特徴があります。

使い方も非常に簡単です。

import net.arnx.jsonic.JSON;

// POJOをJSONに変換します
String text = JSON.encode(new Hoge());

// JSONをPOJOに変換します
Hoge hoge = JSON.decode(text, Hoge.class);

Version 1.2.6 からは、JavaScript内での直接出力用に escapeScript が追加されました。JSONでは許されていない string, number など値の出力やXSS脆弱性を防ぐ<>のエスケープも行われます

// POJOをJavaScriptに変換します()
var value = <%= JSON.escapeScript(value) %>;

JSONICには、JSON操作APIだけでなく、JSONを使ったWebサービスが簡単に構築できるサーブレットも用意されています。詳しくはWebサービスAPIのドキュメントを御覧ください。

ダウンロード

ダウンロードはこちらからできます。なお、JSONICのビルド/実行には、Java 5.0以上が必要です。

JSONエンコーダー

POJOからJSONに変換する場合は、encodeを使います。デフォルトでは、空白などを含まない可読性の低いJSONが出力されますが、二番目の引数をtrueにすることで可読性の高いJSONが出力されるようになります(Pretty Printモード)。

なお、JSONのフォーマット中に何らかの例外が発生した場合は、JSONExceptionでラップされ通知されます(Beanからの取得時に例外発生など)。

// 変換対象のPOJOを準備
Hoge hoge = new Hoge();
hoge.number = 10;      // public field
hoge.setString("aaa"); // public property
hoge.setArray(new int[] {1, 2, 3});

// POJOをJSONに変換します。戻り値は {"number":10,"string":"aaa","array":[1,2,3]}となります
String text = JSON.encode(hoge);

// POJOを可読性の高いJSONに変換します。戻り値は次のような文字列になります
// {
//     "number": 10,
//     "string": "aaa",
//     "array": [1, 2, 3]
// }
String text = JSON.encode(hoge, true); 

// Appendable(StringBuffer, Writerなど)やOutputStreamを出力先にすることもできます(※1)
JSON.encode(hoge, new FileWriter("hoge.txt"));
JSON.encode(hoge, new FileOutputStream("hoge.txt"));
(※1) OutputStreamを指定した場合に出力される文字コードはUTF-8固定となります。 また、close処理は自動では行われませんので必要に応じて別途行う必要があります。

POJOからJSONへの変換ルールは次の通りです。

変換元(Java)変換先(JSON)
Map, DynaBean(※2)object
Object(※3)
boolean[], short[], int[], long[], float[], double[], Object[]array
Iterable (Collection, Listなど)
Iterator, Enumeration
java.sql.Array, java.sql.Struct
char[], CharSequencestring
char, Character
TimeZone, Pattern, File, URL, URI, Type, Member, Charset, UUID
byte[]string (BASE64エンコード)
java.sql.RowIdstring (シリアル化後、BASE64エンコード)
Localestring (言語コード-国コードあるいは言語コード-国コード-バリアントコード)
InetAddressstring (IPアドレス)
byte, short, int, long, float, doublenumber(※4)
Number
Date, Calendarnumber (1970年からのミリ秒)
Enumnumber (ordinalにより変換)
boolean, Booleantrue/false
nullnull
(※2) DynaBeanを利用する場合、Commons BeanUtilsのjarファイルをクラスパスに追加する必要があります。リフレクションを利用して処理を行っているため、利用しない場合は特に含める必要はありません。
(※3) 対象となるインスタンスをパブリック・getterメソッド、パブリック・フィールドの優先順で探索します。staticが付加されたメソッドやフィールド、transientが付加されたフィールドは対象となりません。
(※4) NaN, Infinity, -Infinityに限りそれぞれ文字列"NaN", "Infinity", "-Infinity"に変換されます。

また、org.w3c.dom.Document/ElementからJSONへの変換もサポートしています。詳しくは「高度な使い方 - XMLからJSONへの変換」の項をご覧ください。

なお、JSONはobjectかarrayで始まる必要があるため、直接、intやStringのインスタンスをencodeメソッドの引数に指定した場合エラーとなります。

JSONデコーダー

JSONからPOJOに変換する場合は、decodeを使います。デフォルトでは、object, array, string, number, true/false, nullをHashMap, ArrayList, String, BigDecimal, Boolean, nullに変換しますが、二番目の引数に変換先のクラスを指定することでそのクラスのインスタンスにデータをセットして返してくれます。また、この処理はパブリック・フィールドやパブリック・プロパティ、配列やコレクションのデータを再帰的に辿り実行されますので、一般的なJavaBeansであればencodeして作られたJSONからの逆変換も可能です(Generics型にも対応しています)。

なお、JSON文字列が不正であったり、型の変換に失敗した場合はJSONExceptionが投げられます。

// JSONをPOJOに変換します。戻り値としてサイズが4のArrayListが返されます
List list = (List)JSON.decode("[1, \"a\", {}, false]");

// JSONをHogeクラスのインスタンスに変換します(キャストは不要です)
Hoge hoge = JSON.decode("{\"number\": 10, \"array\": [1, 2, 3]}", Hoge.class);

// クラスの配列型への変換も可能です。
Hoge[] data = JSON.decode("[{ \"id\": 1 }, { \"id\": 2 }, { \"id\": 3 }]", Hoge[].class);

// ReaderやInputStreamからJSONを読み込むことも可能です(※5)
Hoge hoge = JSON.decode(new FileReader("hoge.txt"), Hoge.class);
Hoge hoge = JSON.decode(new FileInputStream("hoge.txt"), Hoge.class);
(※5) InputStreamから読み込む場合の文字コードは、UTF-8/UTF-16BE/UTF-16LE/UTF-32BE/UTF-32LEから自動判別されます。 また、close処理は自動では行われませんので必要に応じて別途行う必要があります。

JSONからPOJOへの変換ルールは次の通りです。

変換元(JSON)指定された型変換先(Java)
objectなし, Object, MapLinkedHashMap
SortedMapTreeMap
その他のMap派生型指定された型
その他の型指定された型(パブリック・フィールド/プロパティに値をセット)(※6)
arrayなし, Object, Collection, ListArrayList
SetLinkedHashSet
SortedSetTreeSet
その他のCollection派生型指定された型
short[], byte[], int[], long[], float[], double[]
Object[]派生型
指定された型
LocaleLocale(「言語コード」「国コード」「バリアントコード」からなる配列とみなし変換)
Mapインデックスの値をキーとするLinkedHashMap
SortedMapインデックスの値をキーとするTreeMap
その他のMap派生型インデックスの値をキーとする指定された型のMap
stringなし, Object, CharSequence, StringString
charchar(幅0の時は'\u0000', 2文字以上の時は1文字目)
CharacterCharacter(幅0の時はnull, 2文字以上の時は1文字目)
AppendableStringBuilder
その他のAppendable派生型指定された型(値をappend)
Enum派生型指定された型(値をEnum.valueOfあるいはint型に変換後Enum.ordinal()で変換)
Date派生型,
Calendar派生型
指定された型(文字列をDateFormatで変換)
byte, short, int, long, float, double,
Byte, Short, Integer, Long, Float, Double,
BigInteger, BigDecimal
指定された型(文字列を数値とみなし変換)
byte[]byte[](文字列をBASE64とみなし変換)
LocaleLocale(文字列を「言語コード」「国コード」「バリアントコード」が何らかの句読文字で区切られているとみなし変換)
PatternPattern(文字列をcompileにより変換)
Class, Charset指定された型(文字列をforNameにより変換)
TimeZoneTimeZone(文字列をTimeZone.getTimeZoneを使い変換)
UUIDUUID(文字列をUUID.fromStringで変換)
File, URI, URL指定された型(文字列をコンストラクタの引数に指定し変換)
InetAddressInetAddress(文字列をInetAddress.getByNameで変換)
boolean, Boolean指定された型("", "false", "no", "off", "NaN"の時false、その他の時true)
numberなし, Object, Number, BigDecimalBigDecimal
byte, short, int, long, float, double,
Byte, Short, Integer, Long, Float, Double,
BigInteger
指定された型
Date派生型,
Calendar派生型
指定された型(数値を1970年からのミリ秒とみなし変換)
boolean, Boolean指定された型(0以外の時true、0の時false)
Enum派生型指定された型(int値をEnum.ordinal()に従い変換)
true/falseなし, Object, BooleanBoolean
char, Character指定された型(trueの時'1'、falseの時'0')
float, double, Float, Double指定された型(trueの時1.0、falseの時NaN)
byte, short, int, long,
Byte, Short, Integer, Long,
BigInteger
指定された型(trueの時1、falseの時0)
booleanboolean
Enum派生型指定された型(trueを1、falseを0とみなしEnum.ordinal()に従い変換)
nullなし, Objectnull
byte, short, int, long, float, double0
booleanfalse
char'\u0000'
(※6) 対象となるインスタンスに対しパブリックなsetterメソッド、パブリックなフィールドの優先順で探索します。 staticやtransientのメソッド/フィールドは対象となりません。 なお、プロパティ名は、単純比較が失敗した場合、LowerCamel記法に変換したものと比較します。

高度な使い方

JSONICでは、フレームワークなどでの利用を想定していくつかの便利な機能を用意しています。

■ 継承による機能拡張

JSONICは、フレームワークでの利用を考慮しインスタンスを生成したり、継承して拡張することができるように設計してあります。 なお、インスタンスを生成して利用する場合は、encode/decodeメソッドの代わりにformat/parseメソッドを利用します。

// インスタンスを生成します
JSON json = new JSON();

// POJOをJSONに変換します(encodeと同じ機能)
String text = json.format(new Hoge());

// POJOを可読性の高いJSONに変換します(Pretty Printモード)
json.setPrettyPrint(true);
String text = json.format(new Hoge());

// JSONをPOJOに変換します(decodeと同じ機能)
Map map = (Map)json.parse(text);

// JSONをHogeクラスのインスタンスに変換します(decodeと同じ機能)
Hoge hoge = json.parse(text, Hoge.class);

DIコンテナなどを使いインスタンスを生成したり、独自の変換を追加するために次のようなオーバーライド可能なメソッドが用意されています。

JSON json = new JSON() {
  
  // フォーマット可能なクラスに変換します(formatでのみ有効です)。
  // 例外が発生した場合、JSONExceptionでラップされ呼び出し元に通知されます。
  protected Object preformat(Context context, Object value) throws Exception {
    // java.awt.geom.Point2DをJSON arrayにフォーマットする例です。
    if (value instanceof Point2D) {
      Point2D p = (Point2D)value;
      List<Double> list = new ArrayList<Double>();
      list.add(p.getX());
      list.add(p.getY());
      return list;
    }
    return super.preformat(context, value);
  }
  
  // 解析されたデータを指定したクラスに変換します(parseでのみ有効です)。
  // 例外が発生した場合、JSONExceptionでラップされ呼び出し元に通知されます。
  // さら下の階層を変換したい場合は、context.convert(キー, 値, 型)を呼び出してください。
  protected <T> T postparse(Context context, Object value,
    Class<? extends T> c, Type t) throws Exception {
    
    // JSON arrayをjava.awt.geom.Point2Dに変換する例です。
    if (Point2D.class.isAssignableFrom(c) && value instanceof List) {
      List list = (List)value;
      Point2D p = (Point2D)create(context, c);;
      p.setLocation(
        context.convert(0, list.get(0), double.class),
        context.convert(1, list.get(1), double.class)
      );
      return c.cast(p);
    }
    return super.postparse(context, value, c, t);
  }
  
  // 型cに対するインスタンスを生成します(parseでのみ有効です)。
  protected <T> T create(Context context, Class<? extends T> c) throws Exception {
    if (Point2D.class.isAssignableFrom(c)) {
      return c.cast(new Point2D.Double());
    }
    return super.create(context, c);
  }
      
  // Class cにおいて、Member mを無視します(parse/formatの両方で有効です)。
  protected boolean ignore(Context context, Class c, Member m) {
    // デフォルトでは、static/transparentのメンバおよびObjectクラスで宣言された
    // メンバの場合、trueを返します。
    return super.ignore(context, c, m);
  }    
};

また、継承して作成した自作クラスをJSON.prototypeにセットすることで、JSON.encodeやJSON.decodeの動作を置き換えることも可能です。

JSON.prototype = MyJSON.class;

■ 総称型を指定してのdecode/parse

decodeやparseの引数にはJava 5.0で追加された総称型も指定できます。しかし、総称型はコンパイル時に削除されてしまうため、decode/parseメソッドの引数として直接的に指定することができません。総称型を使う場合はルート要素をJSON objectにして、対応するクラス定義の中で総称型を使うことをおすすめします。

public class Config() {
    
    public static class RowData {
        public String id;
        public String name;
    }
    
    public List<RowData> rows;
    
    public static Config load(Reader reader) throws IOException {
        return JSON.decode(reader, Config.class);
    }
}

多少トリッキーですが、FieldやMethodや無名クラスからリフレクションで総称型を取得することで間接的に指定する方法もあります。

    private Map<String, Hoge> config;
    
    // Filedを使って総称型を指定
    public Map<String, Hoge> load(Reader reader) throws IOException {
        return JSON.decode(reader, 
            this.getClass().getField("config").getGenericType());
    }
    
    // 総称型を継承した無名クラスを使って総称型を指定
    public List<RowData> load(Reader reader) throws IOException {
	    return JSON.decode("[ { ... }, { ... } ]", 
	        (new ArrayList<RowData>() {}).getClass().getGenericSuperclass());
    }

■ 柔軟な読み込み - TRADITIONALモード

JSONICはポステルの法則(送信するものに関しては厳密に、受信するものに関しては寛容に)に従い、デフォルトでは、妥当でないJSONであっても読み込みが可能なTRADITIONALモードで動作するように作成されています。
RFC 4627に規定された内容との相違点は以下の通りです。

例えば、次のテキストはRFC 4627では無効ですが、JSONICでは読み込むことが可能です。

# database settings
database {
  description: 'ms sql server
	connecter settings'
  user: sa
  password: xxxx // you need to replace your password.
}

/* 
  equals to {"database": {
     "description": "ms sql server\n\tconnecter settings",
     "user": "sa", "password": "xxxx"}}
*/

この動作はsetMode(Mode.STRICT)を指定することで、RFCに準じた妥当性チェックを行なうよう変更することができます。

■ JSONの検証 - STRICTモード

JSONICでは、従来柔軟な読み込みができる反面、RFC 4627に厳密に沿ったJSONであるか判定することができませんでした。 JSONIC 1.2.1からはSTRICTモードが用意され、厳密な検証動作が可能となりました。

モードを変更する場合は、JSONインスタンスのコンストラクタに設定するか、setModeメソッドを呼ぶか、JSON.prototypeにModeを変更したクラスを設定します。

	JSON json = new JSON(JSON.Mode.STRICT);
	
	json.setMode(JSON.Mode.STRICT);
	
	JSON.prototype = (new JSON() {
		{
			setMode(JSON.Mode.STRICT);
		}
	}).getClass();

また、データのデコードを行わず検証のみを行うvalidateメソッドも用意されています(これは、setDepth(0)、setMode(Mode.STRICT)を指定した時と同じです)。

	JSON.validate(new FileInputStream("test.json"));

■ JavaScriptに親和的な出力 - SCRIPTモード

JSONは、可搬性あるデータ連携フォーマットとしてだけでなく、HTML内に書かれるJavaScript内にJavaオブジェクトの内容をインライン出力するためにも便利です。 JSONICでは、このような場合に使いやすいようSCRIPTモードを用意しています。
RFC 4627に規定された内容との相違点は以下の通りです。

モードを変更する場合は、JSONインスタンスのコンストラクタに設定するか、setModeメソッドを呼ぶか、JSON.prototypeにModeを変更したクラスを設定します。また、1.2.6からは、JSON.escapeScript を通じて簡単に使うことが可能です。

	JSON json = new JSON(JSON.Mode.SCRIPT);
	
	json.setMode(JSON.Mode.SCRIPT);
	
	JSON.prototype = (new JSON() {
		{
			setMode(JSON.Mode.SCRIPT);
		}
	}).getClass();
	
	JSON.escapeScript(...);

■ 日時/数値書式の指定 - setDateFormat/setNumberFormat

日付型や数値型は、デフォルトではJSON numberとして出力されますが、JSONIC 1.2.8以降ではsetDateFormat/setNumberFormatを指定することでデフォルトの日時/数値書式を設定できます。フォーマットの書式はそれぞれjava.text.DecimalFormat、java.text.SimpleDateFormatを参照してください(※7)。なお、書式は JSONHint を使うことで上書きすることができます。

	JSON json = new JSON();
	// デフォルトの日時書式を指定
	json.setDateFormat("yyyy/MM/dd");
	
	// デフォルトの数値書式を指定
	json.setNumberFormat("###,##0.00");
	
	// 戻り値は { "date": "2011/01/01", "number": "1,000.00" ] となります
	json.format(new Object() {
		public Date date = new Date(2011, 0, 1);
		public int number = 1000;
	});
(※7) 書式フォーマットは原則SimpleDateFormatと同じですが、ISO8601形式のタイムゾーンを出力するZZもサポートしています。

■ プロパティ名/列挙型出力書式の指定 - setPropertyStyle/setEnumStyle

プロパティ名は、デフォルトではプロパティ名をJSON stringとして、列挙型は序数をJSON numberとして出力しますが、JSONIC 1.2.8以降ではsetPropertyStyle/setEnumStyleを使用することで出力書式を設定できます。

	JSON json = new JSON();
	// プロパティ名を、アッパーキャメル記法に変換して出力
	json.setPropertyStyle(NamingStyle.UPPER_UNDERSCORE);
	
	// 列挙値を、小文字アンダースコア区切りに変換して出力
	json.setEnumStyle(NamingStyle.LOWER_CAMEL);
	
	// 戻り値は { "JSON_MODE": "halfEven" } となります
	json.format(new Object() {
		public RoundingMode jsonMode = RoundingMode.HALF_EVEN;
	});

■ 内部クラスを利用したエンコード/デコード

JSONの設定ファイルを解析したいような場合は、内部クラスやパッケージ・デフォルトのクラスを利用したいことがあります。
JSONICでは、encode/decode/parse/formatの引数に指定されたクラスと同一パッケージの内部クラスや無名クラスを自動的にアクセス可能に変更します。
ただし、この場合に生成された内部クラスのインスタンスには包含するクラスのインスタンスがセットされていない状態になります。内部クラスから包含するクラスのインスタンスにアクセスしたい場合や引数に指定したクラス以外のコンテキストで実行したい場合は、setContextを利用して明示的に指定してください。

public class EnclosingClass {
  public void decode() {
    JSON json = new JSON(); 
    InnerClass ic = json.parse("{\"a\": 100}", InnerClass.class); // このクラスのコンテキストで動作
    
    System.out.println("ic.a = " + ic.a); // ic.a = 100
    
    ic.accessEnclosingClass(); // 実行時にNullPointerExceptionが発生
    
    json.setContext(this);  // コンテキストを設定
    ic = json.parse("{\"a\": 100}", InnerClass.class);
    
    ic.accessEnclosingClass(); // 正常に動作
  }
  
  class InnerClass {
    public int a = 0;
    
    public void accessEnclosingClass() {
      decode(); 
    }
  }
}

■ setMaxDepth - 最大深度の設定

JSONICは、encode/format時に自分自身を戻すようなフィールドやプロパティ、配列を無視することで再帰による無限ループが発生することを防ぎます。 しかし、そのインスタンスにとって孫に当たるクラスが自分のインスタンスを返す場合にも再帰が発生してしまいます。JSONICでは、このような場合へ対処するため 単純に入れ子の深さに制限を設けています。

なお、最大深度の設定はdecode/parse時にも有効ですので深すぎるデータの取得を避けることも可能となります。

この最大深度は、デフォルトでは32に設定されていますが変更することも可能です。

// 5階層以下の情報は取得しない
json.setMaxDepth(5);

■ setSuppressNull - null値の抑制

JSONICでは、format時に値がnullになっているJSON objectのメンバの出力を抑制したり、parse時にnull値の代入を抑制することができます。初期値はfalseです。

余計なメンバが大量に出力されてしまう、プロパティの初期値を優先したいなどの場合に有効です。

// null値の出力や代入を抑制します。
json.setSuppressNull(true);

■ XMLからJSONへの変換

JSONICでは、org.w3c.dom.Document/ElementからJsonMLへの変換をサポートしています。 方法は、通常と同じようにencode/formatの引数にorg.w3c.dom.Document/Elementのインスタンスを設定するだけです。

Document doc = builder.parse(new File("sample.xml"));
String xmljson = JSON.encode(doc);

例えば、下記のXMLの場合

<feed xmlns="http://www.w3.org/2005/Atom">
  <title>Feed Title</title>
  <entry>
    <title>Entry Title</title>
  </entry>
</feed>

次のようなJSONが生成されます(実際にはタグ間の空白文字もTextNodeとして出力されます。不要な場合は、DOM作成時に取り除く必要があります)。

["feed", {"xmlns": "http://www.w3.org/2005/Atom"}, 
	["title", "Feed Title"],
	["entry",
		["title", "Entry Title"],
	]
]

■ JSONHintアノテーション - 変換時ヒントの付加

場合によってデフォルトの変換方式では不十分な場合があります。JSONICでは、メソッドやフィールドにJSONHintアノテーションを付加することで、 動作を部分的に制御することが可能です。

設定できる属性は次の通りです。

属性名値型説明
nameString出力/代入するキー名を変更します
formatString対象の型がNumberあるいはDate型の場合は、指定したフォーマットに従って変換します。
フォーマットの書式はそれぞれjava.text.DecimalFormat、java.text.SimpleDateFormatを参照してください(※8)。
typeClassparse時に指定した型のインスタンスを生成します(対象の型のサブクラスを指定する必要があります)。
ignoreboolean出力/代入対象から除外します
serializedboolean値がJSONであるものとして扱います。デフォルトはfalseです。 Format時はtoString()の値をそのまま出力(※9) 、Parse時は入力されたJSONをJava Objectに変換し再度formatした文字列が設定されます。
anonymString単純値型からMapや複合型に変換するときに単純値型を設定するプロパティ名を指定します。anonymを指定しない場合、Mapの場合はnullキーの値として設定されますが、複合型を指定した場合はエラーとなります。
ordinalintJSON objectへの変換する際のキーの出力順を昇順で指定します。デフォルトはキー値の自然順序順(=負値指定)です。
(※8) 書式フォーマットは原則SimpleDateFormatと同じですが、ISO8601形式のタイムゾーンを出力するZZもサポートしています。
(※9) 出力される文字列は検証されないため妥当でないJSONが出力されてしまう可能性があることに注意してください。 逆に言えば、この機能を使うことでコメントやfunction呼び出しを出力することも可能です。
public class WithHintBean {
  // format/parse時のキー値を変更
  @JSONHint(name="名前")
  public int keyValue = 100;
  
  // format/parse時のフォーマットを指定
  @JSONHint(format="yyyy/MM/dd")
  public Date dateValue = new Date();
    
  // 数値の時は、DecimalForamtとして認識される
  @JSONHint(format="##0.00")
  public int numberValue = 100;
    
  // 配列やリストでもOK
  @JSONHint(format="yyyy/MM/dd")
  public List<Date> dateArray;
  
  // メソッドにも付与可能(getter/setterで別のヒントを与えることも可)
  @JSONHint(format="yyyy/MM/dd")
  public int getMethodValue() {
    return 100;
  }
  
  // ArrayListの代わりにLinkedListのインスタンスを生成
  @JSONHint(type=LinkedList.class)
  public List<String> stringList;
    
  // format/parse時に無視
  @JSONHint(ignore=true)
  public int ignoreValue = 100;
    
  // 値はJSON
  @JSONHint(serialized=true)
  public String json = "{\"num\": 100, \"func\": sum(100, 200) /*illegal JSON*/}";
}

■ JSONHintによるString指定 - データの文字列化

JSONHintアノテーションのtype属性にStringを指定することで、データをtoString()およびString型を引数にとるコンストラクタを取る文字列相当型として扱うことができるようになります。

public class TestBean {
  @JSONHint(type=String.class)
  public StringBean sb;
}

public class StringBean {
  // decode時は、String型を引数に取るコンストラクタが呼ばれます
  public StringBean(String str) {
    ...
  }
  
  // encode時は、toStringが呼ばれます
  public String toString() {
    ...
  }
}

■ JSONHintによるSerializable指定 - データの部分シリアル化

JSONHintアノテーションのtype属性にjava.io.Serializableを指定することで、データをObjectInputStream/ObjectOutputStreamによりシリアル化されたバイト列データとして取り扱うことができます(バイト列はBase64でエンコードされJSON stringとして出力されます)。

public class TestBean {
  @JSONHint(type=Serializable.class)
  public SerializableBean sb;
}

この機能を使うことで、JSON化が困難なオブジェクトもJSON-RPCなどでやり取りすることが可能となります。

JSONIC for AS3

JSONICでは、クライアントにAdobe FlexやAdobe Airを使用する場合を想定し、AS3版のJSONクラスとRemoteObjectライクなJSON-RPCクライアントライブラリを同梱しています。

■ ActionScript3版 JSONライブラリ

Java版JSONクラスとほぼ同じです。ただし、decode/parseメソッドでの特定クラスへの変換はサポートしていません(動的型で十分だと思いますので……)。

import net.arnx.jsonic.JSON;

// インスタンスをJSONに変換します
var text:String = JSON.encode(new Hoge());

// JSONをObjectに変換します
var hoge:Object = JSON.decode(text);

■ ActionScript3版 JSON-RPCクライアントライブラリ

RemoteObjectライクなJSON-RPCクライアントクラスです。使い方はmx:RemoteObjectやmx:WebServiceなどを参考にしてください(lastResultのBindingも可能です)。

<mx:WindowedApplication xmlns:mx="http://www.adobe.com/2006/mxml" 
  xmlns:js="http://arnx.net/jsonic">
    
  <!-- sourceにコンポーネント名、Operationのnameにメソッド名を指定します。 -->
  <js:WebService id="remote" 
    endpoint="http://localhost:8080/sample/rpc/rpc.json" 
    source="calc" 
    makeObjectsBindable="false" 
    showBusyCursor="true"
    result="onResult(event)"
    fault="onFault(event)">
    
    <js:Operation name="plus" result="onResult(event)" fault="onFault(event)" />
    ...
  </js:WebService>
    
  <mx:Button label="実行" click="remote.plus(100, 200)" />
  <mx:TextInput id="output" text="{remote.plus.lastResult}" />
    
  <mx:Script>
    <![CDATA[
      private function onResult(event:ResultEvent) {
        trace(event.result); // 100+200 => 300
      }
      
      private function onFault(event:FaultEvent) {
        trace(event.fault.message);
      }
    ]]>
  </mx:Script>
</mx:WindowedApplication>

FAQ

Q. RESTServletでHTTP GETを使うと日本語が文字化けします
A. 入力文字エンコーディングの問題です。特にApache Tomcat5以降は仕様を厳密に解釈した結果、GETがsetCharacterEncodingを無視するという問題がありますのでuseBodyEncodingForURIを設定し回避する必要があります(JSONIC 1.1 ではGETパラメータを独自に解析していたため、この問題は発生していませんでした)。
Q. 大量データを読み込むとOutOfMemoryErrorで落ちます。
A. JSONICは、ブラウザをクライアントとしたような大規模データの送受信を行わないシステムでの利用を想定して作成されています。XMLにおいてDOMに対してSAXやStAXがあるように、大量データを取り扱う場合にはストリームAPIを利用すべきですがJSONICには現状用意されておりませんので、Twitter Streaming APIのように行区切りのJSONで送受信するか、jacksonなど代替ライブラリの採用を検討ください。
Q. Resin サーバで RESTServletやRPCServletが動作しません。
A. Resin サーバでは、web.xml中にある ${...} を変数として扱うため、\${...} と書かないといけないようです。

Mavenリポジトリ

JSONICは、1.2.7以降 Maven Central Repository に登録されるようになりました。groupId、artifactIdは次の通りです。

<groupId>net.arnx</groupId>
<artifactId>jsonic</artifactId>

それ以前のバージョンに関しては、 Seasar.orgのMavenリポジトリ などをご利用ください。

ライセンス

JSONICは、Apache License, Version 2.0下で配布します。

自分のライブラリへの組み込んでいただいたり、その際にパッケージ名の変更や処理の変更など行っていただいて構いません。保障はありませんが、ライセンスの範囲内でご自由にお使いください。

バグ・要望の報告先

バグや要望などはJSONICプロジェクトサイトチケットに報告ください。

リリースノート

2012/1/17 version 1.2.11

2012/1/17 version 1.2.10

2012/1/17 version 1.2.9

2011/12/17 version 1.2.8

2011/10/9 version 1.2.7

2011/8/7 version 1.2.6

2010/12/19 version 1.2.5

2010/10/19 version 1.2.4

2010/10/05 version 1.2.3

2010/10/02 version 1.2.2

2010/09/04 version 1.2.1

2010/04/04 version 1.2.0

WebServiceServletは、JSONIC 1.2以降は非推奨となりました。互換性維持のために以前同様の形で利用可能ですが、今後はRESTServletおよびRPCServletを利用してください。