OpenOffice Calcのodsファイルは設定情報、スタイル情報、ドキュメント本体などのxmlファイルやリソースをzipで固めたものです。 odsファイルの拡張子をzipに変換して解凍すると、それぞれのファイルにアクセスできます。 ドキュメント本体のファイルはcontent.xmlです。 ドキュメントを更新するたびに手作業でcontent.xmlを取り出すのは面倒なので、javaの標準ライブラリでメモリに展開します。
下のコードはサンプルのOdsReaderクラスから抜粋したものです。 まず、ZipInputStreamクラスでodsファイルを開きます。 そしてエントリー名がcontent.xmlのファイルを探し、byte配列に読み込みます。
private byte[] readContent(String fileName) { byte[] res = null; ZipInputStream zipInStream = null; try { zipInStream = new ZipInputStream(new BufferedInputStream(new FileInputStream(fileName) ) ); ZipEntry entry = null; while( (entry = zipInStream.getNextEntry() ) != null) { // エントリー名が「content.xml」の場合、中身を読む String entryName = entry.getName(); if(entryName.equals("content.xml") ) { res = readFromZipInputStream(zipInStream); } zipInStream.closeEntry(); } } catch(Exception exc) { exc.printStackTrace(); } try { zipInStream.close(); } catch(Exception exc) { exc.printStackTrace(); } return res; } // ZipInputStreamのエントリーからbyte配列に読み込む private byte[] readFromZipInputStream(ZipInputStream in) throws Exception { byte[] res = null; byte[] buff = new byte[BUFF_SIZE]; int readSize; while(true) { readSize = in.read(buff); if(0 < readSize) { // byte配列の連結 res = concat(res, buff, readSize); } else { break; } } return res; }
byte配列を解釈してDOMのDocumentを作成します。 パーサに直接byte配列を渡すことはできないので、ByteArrayInputStreamでラッピングしてから渡します。
try { // odsファイルから「content.xml」を抜き出してbyte型配列に記憶 byte[] xmlBytes = readContent(fileName); // byte配列をストリームクラスでラッピング ByteArrayInputStream bis = new ByteArrayInputStream(xmlBytes); DocumentBuilderFactory dbfactory = DocumentBuilderFactory.newInstance(); DocumentBuilder builder = dbfactory.newDocumentBuilder(); _xmldoc = builder.parse(bis); } catch(Exception exc) { exc.printStackTrace(); }
DOMのDocumentにある目的のシートにアクセスします。 複雑なドキュメントは見ていませんが、content.xmlは大体以下のような形式になっているようです。
<?xml version="1.0" encoding="UTF-8"?> <office:document-content xmlns:office=...> <office:scripts/> <office:font-face-decls> ... フォント情報 </office:font-face-decls> <office:automatic-styles> ... スタイル情報、スタイル名毎に設定を記録 </office:automatic-styles> <office:body> <office:spreadsheet> <table:table table:name="シート名" table:style-name="スタイル名" ...> <table:table-column table:style-name="スタイル名" .../> table:table-column要素(列ヘッダ情報)の繰り返し... <table:table-row table:style-name="スタイル名"> <table:table-cell ...>...</table:table-cell> セル要素の繰り返し... </table:table-row> table:table-row要素(行情報)の繰り返し... </table:table> table:table要素(シート情報)の繰り返し... </office:spreadsheet> </office:body> </office:document-content>
目的のシートにアクセスするには、table:tableタグをリストアップしてtable:name属性がシート名と一致するものを探します。
Element sheetElement = null; // シートの情報は「table:table」タグに収められている NodeList nodeList = _xmldoc.getElementsByTagName("table:table"); for(int i = 0; i < nodeList.getLength(); i++) { Element elem = (Element)nodeList.item(i); // シート名は「table:table」タグの「table:name」属性に収められている String nameAttr = elem.getAttribute("table:name"); if(nameAttr.equals(sheetName) ) { sheetElement = elem; break; } } ... sheetElementを使って処理