【エラー解決方法】JAXBでXMLファイルが解読できない!?を解決する方法
目次
先日バッチでエラーが発生したとメールが送信されてきました。
どうやらXMLファイル取得後に、パースができなくてエラーとなっているようです。
メールの内容は以下になります。
javax.xml.bind.UnmarshalException
– with linked exception: [org.xml.sax.SAXParseException; lineNumber: 1; columnNumber: 1;
プロローグにはコンテンツを指定できません。]
XMLファイルを取得し、JAXBを使ってオブジェクトにパースしている(unmarshal)のですが、そこでエラーとなっているようです。
エラーとなったファイル以外にも複数個ファイルを取得していますが、なぜか1つだけエラーとなっていました。
エラー発生時のプログラム
// ファイル読み込み
File file = new File(System.getProperty("user.dir") + "/test/xml/file.xml");
FileReader filereader = new FileReader(file);
int ch;
StringBuffer sb = new StringBuffer();
// 文字読み込み
while((ch = filereader.read()) != -1) {
sb.append((char)ch);
}
// ファイルクローズ
filereader.close();
// エンティティにパース
AbcEntity entity = JAXB.unmarshal(new StringReader(sb.toString()), AbcEntity.class);
17行めでエラーとなっていました。
JAXBとは
JAXB(Java Acrchitecture for XML Binding)
XMLとJavaオブジェクトを相互変換するためのAPIで、Java SE6からは標準ライブラリに組み込まれています。
エラー発生時の環境
エラー発生時の開発環境は以下のとおりです。
- 言語
- Java1.7.0_51
原因
取得したXMLファイルがBOM付きで保存されていたため、unmarshal時に先頭3バイトの文字列が読めずエラーとなっていた。
他にもいくつかファイルを取得しているのですが、エラーとなったファイルは1つだけなので、このファイルだけテキストエディタなどで一度開いてからBOM付きで保存したと思われます。
解決方法
・解決方法1
XMLファイル提供元に、BOM付きでファイルを作成しないように依頼をする。
・解決方法2
やっぱりシステム屋なので、BOM付きファイルに対応したプログラムを作りなおす。
ということでBOM付きファイルが来た場合に、先頭部分を読み飛ばすプログラムを作成しました。
// ファイル読み込み
File file = new File(System.getProperty("user.dir") + "/test/xml/test_file.xml");
InputStream fin = new FileInputStream(file);
Reader in = new InputStreamReader(fin, "UTF-8");
int ch;
StringBuilder buf = new StringBuilder();
// 文字読み込み
ch = in.read();
if (ch != -1) {
// BOM(0xFEFF)以外
if (ch != 0xFEFF) {
buf.append((char) ch);
}
while ((ch = in.read()) != -1) {
buf.append((char) ch);
}
}
// ファイルクローズ
try {
if (in != null) {
in.close();
} else if (fin != null) {
fin.close();
}
} catch (Exception ex) {
System.out.println(ex.getMessage());
}
// エンティティにパース
AbcEntity xbEntity = JAXB.unmarshal(new StringReader(buf.toString()), AbcEntity.class);
これでunmarshal時にエラーにならなくなりました。
まとめ
仕様書にBOM付きか無しか書いていない場合には、どちらのファイルが来てもエラーとならないようにプログラム側で対応できるようにしておく必要があります。
エラーが出てから気がつくではなく、先に考えておけばよかったなと思います。
まあ今回はBOM無しでという仕様だったので対応していなかったんですけどね・・・。