需求:
1. 对XML中的某些节点和属性进行特殊的检查;
> 简单的检查: 不依赖XML上下文;
> 复杂的检查1: 依赖XML中的前序内容(先声明再使用);
> 复杂的检查2: 依赖XML中的后序内容(先使用再声明);
2. 友好的错误信息
> 错误信息支持中文;
> 至少需要将出错点定位到行号;
3. 检查整个XML, 最后统一将错误信息收集成一个XML
分析:
1. 简单属性或节点的检查: 需要参与节点和属性的检查过程;
2. 复杂查检1: 需要在参与节点和属性检查的过程中, 记录相应的上下文信息, 供后面的检查使用;
3. 复杂检查2 + 错误定位到哪行: 需要在参与节点和属性检查的过程中, 获取当前的位置信息, 并存储下来, 供检查时出错处理;
4. 错误提示中文: 需要使用中文的properties文件, 需要修改获取message常量使用中文格式.
实现的XML Schema Validator的工具:
1. MSV: SUN实现的一套工具.
2. Xerces-2
最终决定基于MSV进行扩展.
MSV的扩展
1. 根据schema文件创建一个Verifier
VerifierFactory factory = new com.sun.msv.verifier.jarv.TheFactoryImpl();
Verifier verifier = factory.newVerifier(CustomErrorReporter.class.getResourceAsStream(SCHEMA));
2. 定制错误处理
verifier.setErrorHandler(new ReportErrorHandler(){
public void error(SAXParseException arg0) throws SAXException {
System.out.println("line: " + arg0.getLineNumber() + "; column: " + arg0.getColumnNumber() + "; message: " + arg0.getMessage());
}
public void fatalError(SAXParseException arg0) throws SAXException {
System.out.println("line: " + arg0.getLineNumber() + "; column: " + arg0.getColumnNumber() + "; message: " + arg0.getMessage());
}
});
3. 深入检查的各个阶段(通过SAX pipe line来实现)
// create a SAX parser
SAXParserFactory parserFactory = SAXParserFactory.newInstance();
parserFactory.setNamespaceAware(true);
XMLReader reader = parserFactory.newSAXParser().getXMLReader();
// then setup the SAX pipe line as follows:
//
// parser ==> interceptor ==> verifier
//
// "interceptor" works as a SAX filter.
Interceptor interceptor = new Interceptor();
interceptor.setParent(reader);
interceptor.setContentHandler(verifier.getVerifierHandler());
其中, Interceptor就是我们可以定制特殊处理的点. 它的定义如下:
private static class Interceptor extends XMLFilterImpl {
private Locator locator = null;
public void setDocumentLocator (Locator locator)
{
this.locator = locator;
super.setDocumentLocator(locator);
}
public void startElement(String ns, String local, String qname,
Attributes atts) throws SAXException {
super.startElement(ns, local, qname, atts);
}
public void endElement(String ns, String local, String qname)
throws SAXException {
super.endElement(ns, local, qname);
}
public void characters(char[] buf, int start, int len)
throws SAXException {
super.characters(buf, start, len);
}
}
4. 检查XML
interceptor.parse(new InputSource(CustomErrorReporter.class.getResourceAsStream(DATA)));
5. 错误提示使用中文
可以使用Locale.setDefault(Locale.CHINA)来设置默认的Locale
同时在这个类的同一个包中增加Message_zh_CN.properties文件, 这个文件将中文变成unicode. 可以使用jdk下的工具native2ascii来实现.