えー、久々にお仕事ネタです。(たまには書かないと・・・)
Javaで、XMLのエスケープ処理とかの実装を調査する機会がありまして、ちょっとハマったので、覚書としてメモしておきます。
XML文書をパースする際には、定義済み実体参照を置換すること(例えば、<を<にするとか)は理解していたんですが、文字参照(&#で始まる10進や、&#xで始まる16進)を文字に置換するというのを忘れておりまして。
そこで、16進数字から文字へ変換する処理を実装しようと、APIを探したんですが、コレ!というのが見当たりません。ググってもなかなかヒットしない。逆(文字から16進数字)は、Integer#toHexStringでできるんですが・・・。
じゃ自作するか、と同僚とアレコレやってみました。intにしてbyte配列にしてビット演算して・・・と試行錯誤して何とか出来たは出来たんですが、イマイチすっきりしなくて。
ダメ元で、もう一度キーワードを変えてググったら・・・やっとよさげなのにヒットしました。
decodeメソッドだけ抜粋。
// NCRデコードを行う(10進、16進)
public static String decode(String str) {
Pattern pattern = Pattern.compile("&#(\\d+);|&#([\\da-fA-F]+);");
Matcher matcher = pattern.matcher(str);
StringBuffer sb = new StringBuffer();
Character buf;
while(matcher.find()){
if(matcher.group(1) != null){
buf = new Character(
(char)Integer.parseInt(matcher.group(1)));
}else{
buf = new Character(
(char)Integer.parseInt(matcher.group(2), 16));
}
matcher.appendReplacement(sb, buf.toString());
}
matcher.appendTail(sb);
return sb.toString();
}
なるほど。Integer#parseIntした値をcharでキャストしてCharacterに食わせればいいのか・・・うーん、こんなに簡単だったとは・・・。
しかも、「&#x○○○○;」を抽出する正規表現のロジックも載ってるし。Matcherってこうやって使うのね。appendReplacementか・・・勉強になります。
以上、覚書でした。
なんか、今どきXMLパース処理なんて、”車輪の再発明”しているような気がしないでもないですが、XML1.0勧告をちゃんと読むいい機会になりました。
apache の commons-lang3 にあるようです。
http://commons.apache.org/lang/api-release/org/apache/commons/lang3/StringEscapeUtils.html#unescapeHtml4(java.lang.String)