JSON文字列とJavaオブジェクトとの相互変換
前置き
JSON文字列とJavaオブジェクトとの相互変換することができるJSON-libは、Java以外のアプリケーションとインターネット経由であれこれデータをやりとりするのになかなかシンプルで良い。
gihyo.jpの記事で出ている例のようにクライアントのJavaScriptとやりとりするサーバを開発するときなどはそのままスムーズに利用することができる。
しかし、今度はJavaでJSON形式で公開されているWebAPIを利用する際にたまに歯がゆい思いをすることがある。
{ first_name: 'Yuan', last_name: 'Ying' age: 17 }
例えば、上のようなJSONオブジェクトを返すWebAPIをJavaのクライアントから利用したかった場合、
class Person {
private String firstName;
private String lastName;
private int age;
// and setter and getter...
}
上記のようなPersonクラスにマッピングしたくなるのが人情というものだろう。
けどそのままではうまくいかないハズ。
CamelCase
なぜなら、JSONオブジェクトのプロパティはアンダースコアで単語と単語を分けているが、PersonクラスのプロパティはCamelCase形式だから。なのでPersonクラスを以下のように書き換えればうまくいく。
class Person {
private String first_name;
private String last_name;
private int age;
// and setter and getter...
}
けどすっごくダサイ。
JavaIdentifierTransformer
JSON-libではそんな場合に備えてnet.sf.json.util.JavaIdentifierTransformer
というクラスが用意されている。このクラスはJSONオブジェクトをJavaクラスに変換する際にプロパティ名も変換してくれる。
デフォルトでは5つの変換方法が定義されていて、利用する際には以下のようにJSONUtilsクラスに登録する必要がある。
JSONUtils.setJavaIdentifierTransformer(JavaIdentifierTransformer.CAMEL_CASE);
JSONObject json = ...;
Person person = JSONObject.toBean( json, Person.class, classMap );
上記のようにアンダースコアなプロパティ名をCamelCase形式変換してくれるJavaIdentifierTransformerはデフォルトでは用意されていないので自分で用意する必要がある。
import net.sf.json.util.JavaIdentifierTransformer;
/**
* @author yuanying
*
*/
public class CamelCaseJavaIdentifierTransformer extends
JavaIdentifierTransformer {
/* (non-Javadoc)
* @see net.sf.json.util.JavaIdentifierTransformer#transformToJavaIdentifier(java.lang.String)
*/
@Override
public String transformToJavaIdentifier(String str) {
if( str == null ){
return null;
}
String str2 = shaveOffNonJavaIdentifierStartChars( str );
char[] chars = str2.toCharArray();
int pos = 0;
StringBuffer buf = new StringBuffer();
boolean toUpperCaseNextChar = false;
while( pos < chars.length ){
if( !Character.isJavaIdentifierPart( chars[pos] )
|| (chars[pos] == '_')
|| Character.isWhitespace( chars[pos] ) ){
toUpperCaseNextChar = true;
}else{
if( toUpperCaseNextChar ){
buf.append( Character.toUpperCase( chars[pos] ) );
toUpperCaseNextChar = false;
}else{
buf.append( chars[pos] );
}
}
pos++;
}
return buf.toString();
}
}
バグ発見
上記のクラスを試してみると、うまくいかない。何故か知らんがぬるぽが出る。
仕方なしにJSON-libのソースを読んでみるに、JSON-lib 1.1のnet.sf.json.JSONObject
の513行目と598行目にバグがあるとしか思えない。
以下のように直す。
Class type = (Class) props.get( name );
// FIX Class type = (Class) props.get( key );
これでうまくいった。