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自体にもスキーマが存在すればこそできることです。
