XML文書の妥当性検証
XMLスキーマを使用してXML文書の妥当性を検証するには、JAXPのAPIを使用すると可能になります。ツールとして利用するには、XML文書やXMLスキーマ文書を指定できるように工夫することで実現できますが、今回は固定したファイルパスを使い妥当性を検証します。リスト3がJAXPを使ったXML文書を検証し結果を表示するためのプログラムです。
001:package jp.kawakubo.xmlschema; 002: 003:import java.io.IOException; 004:import java.io.PrintWriter; 005:import javax.servlet.ServletException; 006:import javax.servlet.http.HttpServlet; 007:import javax.servlet.http.HttpServletRequest; 008:import javax.servlet.http.HttpServletResponse; 009:import javax.xml.parsers.DocumentBuilder; 010:import javax.xml.parsers.DocumentBuilderFactory; 011:import org.xml.sax.ErrorHandler; 012:import org.xml.sax.SAXException; 013:import org.xml.sax.SAXParseException; 014: 015:public class MyXMLValidator extends HttpServlet { 016: 017: private static final String SOURCE_PATH = "file:///Users/tomoharu/Documents/XMLSample/Meigara2.xml"; 018: private static final String XSD_PATH = "file:///Users/tomoharu/Documents/XSLSample/sample1.xsd"; 019: 020: protected void processRequest(HttpServletRequest request, HttpServletResponse response) 021: throws ServletException, IOException { 022: 023: response.setContentType("text/html;charset=UTF-8"); 024: final PrintWriter out = response.getWriter(); 025: 026: DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); 027: dbf.setValidating(true); 028: dbf.setNamespaceAware(true); 029: dbf.setAttribute("http://java.sun.com/xml/jaxp/properties/schemaLanguage" , 030: "http://www.w3.org/2001/XMLSchema"); 031: dbf.setAttribute( 032: "http://java.sun.com/xml/jaxp/properties/schemaSource", XSD_PATH); 033: 034: DocumentBuilder db = null; 035: 036: try { 037: db = dbf.newDocumentBuilder(); 038: } catch ( javax.xml.parsers.ParserConfigurationException ex ) { 039: System.err.println("DocumentBuilderの生成に失敗しました:" + ex.getMessage()); 040: return; 041: } 042: 043: db.setErrorHandler(new ErrorHandler() { 044: // リスト4でErrorHandlerのメソッドを実装しています 045: } 046: 047: try { 048: db.parse(SOURCE_PATH); 049: try { 050: out.println("<html>"); 051: out.println("<head>"); 052: out.println("<title>Servlet MyXMLValidator</title>"); 053: out.println("</head>"); 054: out.println("<body>"); 055: out.println("<h1>JAXPを使ってのXML文書の妥当性検証結果</h1>"); 056: out.println("<h2>正常終了</h2>"); 057: out.println("</body>"); 058: out.println("</html>"); 059: } finally { 060: out.close(); 061: } 062: } catch (SAXException e) { 063: e.printStackTrace(); 064: } 065: 066: } 067: 068: // HttpServlet のメソッドは紙面都合上割愛しています 069: 070:}
26~32行目までのDocumentBuilderFactoryをインスタンス化し、フィールドに値を設定しているところが今回のJAXPの肝になります。28行目で名前空間を認識させ、29、30行目でスキーマ言語としてW3C XML Schemaの使用を指定しています。31、32行目でXMLスキーマ文書のパスを指定しています。
次の肝が、34~45行目です。34~41行目でDocumentBuilderをインスタンス化しています。43行目でErrorHandlerを設定していますが、設定しなくても警告が出るのみです。ただし、今回のように、妥当性を検証するのにErrorHandlerがあるのに使用しないのはもったいない話です。ErrorHandlerを無名の内部クラスとして定義していますが、作り的に違和感がある方は適宜書き換えてください。
001:db.setErrorHandler(new ErrorHandler() { 002: public void error(SAXParseException exception) { 003: printResult(exception, "ERROR"); 004: } 005: 006: public void fatalError(SAXParseException exception) { 007: printResult(exception, "FATAL"); 008: } 009: 010: public void warning(SAXParseException exception) { 011: printResult(exception, "WARNING"); 012: } 013: 014: private void printResult(SAXParseException exception, String category) { 015: try { 016: out.println("<html>"); 017: out.println("<head>"); 018: out.println("<title>Servlet MyXMLValidator</title>"); 019: out.println("</head>"); 020: out.println("<body>"); 021: out.println("<h1>JAXPを使ってのXML文書の妥当性検証結果</h1>"); 022: out.println("<table border='1'>"); 023: out.println("<tr>"); 024: out.println("<td>区分</td>"); 025: out.println("<td>" + category + "</td>"); 026: out.println("</tr>"); 027: out.println("<tr>"); 028: out.println("<td>URI</td>"); 029: out.println("<td>" + exception.getSystemId() + "</td>"); 030: out.println("</tr>"); 031: out.println("<tr>"); 032: out.println("<td>行</td>"); 033: out.println("<td>" + exception.getLineNumber() + "</td>"); 034: out.println("</tr>"); 035: out.println("<tr>"); 036: out.println("<td>列</td>"); 037: out.println("<td>" + exception.getColumnNumber() + "</td>"); 038: out.println("</tr>"); 039: out.println("<tr>"); 040: out.println("<td>メッセージ</td>"); 041: out.println("<td>" + exception.getMessage() + "</td>"); 042: out.println("</tr>"); 043: out.println("</table>"); 044: out.println("</body>"); 045: out.println("</html>"); 046: } finally { 047: out.close(); 048: } 049: } 050: 051:});
この無名内部クラスで難しい部分はありません。ErrorHandlerにはerror、fatalError、warningの3つがあります。それぞれ引数としてSAXParseExceptionを取ります。3つのメソッドで引数が同じのためprintResultというメソッドを用意し、exceptionがERRORかFATAL ERRORか WARNINGか識別できるよう第2引数に設定しています。
printResultの内容は単にSAXParseExceptionのメソッドである、getSystemId、getLineNumber、getColumnNumberと継承しているSAXExceptionのメソッドであるgetMessageメソッドを使用し表示しているのみです。
面白いのはXML文書だけでなくXMLスキーマ文書の間違いも例外として補足され表示されることです。従って、スキーマを作成したもののスキーマ自体の妥当性が分からないという場合も、このツールが使用できます。つまり、W3C XML Schema自体にもスキーマが存在すればこそできることです。