我需要支持用户向我提交无效XML文件的情况,并向他们报告有关错误的信息。理想情况下,错误的位置(行号和列号)和错误的性质。
当标记丢失或类似错误时,我的示例代码(见下文)运行良好。在这种情况下,我得到一个大概的位置和一个有用的解释。但是,当XML文件包含非UTF-8字符时,我的代码会失败。在这种情况下,我得到一个无用的错误:
com.sun.org.apache.xerces.internal.impl.io.MalformedByteSequenceException: Invalid byte 1 of 1-byte UTF-8 sequence.
我找不到确定无效字符可能的行号的方法,也找不到字符本身。有没有办法做到这一点?
如果正如一条评论所暗示的那样,可能无法实现解析步骤,是否有办法处理XML文件,而不是使用解析器,而只是逐行处理,寻找和报告非UTF-8字符?
示例代码如下。首先是一个基本的错误处理程序:
public class XmlErrorHandler implements ErrorHandler {
@Override
public void warning(SAXParseException e) throws SAXException {
show("Warning", e); throw e;
}
@Override
public void error(SAXParseException e) throws SAXException {
show("Error", e); throw e;
}
@Override
public void fatalError(SAXParseException e) throws SAXException {
show("Fatal", e); throw e;
}
private void show(String type, SAXParseException e) {
System.out.println("Line " + e.getLineNumber() + " Column " + e.getColumnNumber());
System.out.println(type + ": " + e.getMessage());
}
}
一个简单的测试程序:
public class XmlTest {
public static void main(String[] args) {
try {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser parser = spf.newSAXParser();
XMLReader reader = parser.getXMLReader();
reader.setContentHandler(new DefaultHandler());
reader.setErrorHandler(new XmlErrorHandler());
InputSource is = new InputSource(args[0]);
reader.parse(is);
}
catch (SAXException e) { // Useful error case
System.err.println(e);
e.printStackTrace(System.err);
}
catch (Exception e) { // Useless error case arrives here
System.err.println(e);
e.printStackTrace();
}
}
}
示例XML文件(使用来自(例如)Word文档的非UTF-8智能引号):
<?xml version="1.0" encoding="UTF-8"?>
<example>
<![CDATA[Text with <91>smart quotes<92>.]]>
</example>
答案 0 :(得分:0)
我在确定XML文件中的问题使用了几种方法方面取得了一些成功。
调整我的问题中的代码以使用带有ContentHandler
的本地Locator
(见下文),证明XML正在处理,直到遇到无效字符。特别是,正在跟踪行号。保留行号允许在发生有问题的异常时从ContentHandler
检索它。
此时,我提出了两种可能性。第一种是在InputStream
上使用不同的编码重新运行处理,例如。 Windows-1252
。在这种情况下,解析完成且没有错误,我能够检测具有已知问题的行上的字符。这允许向用户提供合理有用的错误消息,即。行号和字符。
我的第二种方法是将最高等级答案的代码调整为this SO question。此代码允许您在字节流中查找第一个非UTF-8字符。如果您假设0x0A
(换行)代表XML中的新行(这在实践中看起来效果很好),则可以轻松提取行号,列号和无效字符以获得精确错误信息。
// Modified test program
public class XmlTest {
public static void main(String[] args) {
ErrorFinder errorFinder = new ErrorFinder(0); // Create our own content handler
try {
SAXParserFactory spf = SAXParserFactory.newInstance();
SAXParser parser = spf.newSAXParser();
XMLReader reader = parser.getXMLReader();
reader.setContentHandler(errorFinder); // Use instead of the default handler
reader.setErrorHandler(new XmlErrorHandler());
InputSource is = new InputSource(args[0]);
reader.parse(is);
}
catch (SAXException e) { // Useful error case
System.err.println(e);
e.printStackTrace(System.err);
}
catch (Exception e) { // Useless error case arrives here
System.err.println(e);
e.printStackTrace();
// Option 1: repeat parsing (see above) with a new ErrorFinder initialised thus:
ErrorFinder ef2 = new ErrorFinder(errorFinder.getCurrentLineNumber()); // and
is.setEncoding("Windows-1252");
}
}
}
// Content handler with irrelevant method implementations elided.
public class ErrorFinder implements ContentHandler {
private int lineNumber; // If non-zero, the line number to retrieve characters for.
private int currentLineNumber;
private char[] chars;
private Locator locator;
public ErrorFinder(int lineNumber) {
super();
this.lineNumber = lineNumber;
}
public void setDocumentLocator(Locator locator) {
this.locator = locator;
}
@Override
public void startDocument() throws SAXException {
currentLineNumber = locator.getLineNumber();
}
... // Skip other over-ridden methods as they have same code as startDocument().
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
currentLineNumber = locator.getLineNumber();
if (currentLineNumber == lineNumber) {
char[] c = new char[length];
System.arraycopy(ch, start, c, 0, length);
chars = c;
}
}
public int getCurrentLineNumber() {
return currentLineNumber;
}
public char[] getChars() {
return chars;
}
}