如何通过Java SAXParser解析dtd验证的XML文档时解决“问号”?

时间:2014-08-10 19:55:52

标签: java xml sax dtd

我想知道为什么我的SaxParser似乎无法解析外部dtd文件中定义的某些实体。我正在处理一个巨大的xml文件,其中包含以下标题。所以输入是(大大减少: - )):

// myxml.xml
<?xml version="1.0" encoding="ISO-8859-1"?>
<!DOCTYPE authors SYSTEM "mydtd.dtd">
<authors>
    <author>
        Bal&aacute;zs
    </author>
</authors>

这是错误的输出:

Bal
?zs

显然&aacute;未解决!

这就是我设置解析器的方式:

// MySaxParser.java

public class MySaxParser extends DefaultHandler {

@Override
public void characters(char[] ch, int start, int length)
        throws SAXException {
    if ("author".equals(currentTag)) {
        System.out.println(String.valueOf(Arrays.copyOfRange(ch, start, start + length)));
    }
}

static public void main(String[] args) throws Exception {
    SAXParserFactory spf = SAXParserFactory.newInstance();
    spf.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, false);
    spf.setNamespaceAware(true);
    spf.setValidating(true); // From what I understood from the API this combined
                             // with '<!DOCTYPE mydtd SYSTEM "mydtd.dtd">' from
                             // the file myxml.xml should do the trick. What do I miss?

    SAXParser saxParser = spf.newSAXParser();
    XMLReader xmlReader = saxParser.getXMLReader();
    xmlReader.setContentHandler(new SAXLocalNameCount());
    xmlReader.setErrorHandler(new MyErrorHandler(System.err));

    xmlReader.parse("file:/path/to/myxml.xml");
}
}

我想念什么?我是否必须做多spf.setValidating(true)以使解析器知道xml文件头中定义的dtd?

我应该提到dtd和xml在语法和语义上都是正确的。 dtd包含<!ENTITY aacute "&#225;" ><!-- small a, acute accent -->作为解析的映射。我从可靠来源下载了文件,因此错误必须在我的代码中。

更新

正如@eckes建议的那样,我通过

将字符的int值传递给方法characters
@Override
public void characters(char[] ch, int start, int length)
        throws SAXException {
    if ("author".equals(currentTag)) {
        for (int i = start; i < length; i++) {
            System.out.println(ch[i] + " - " + Character.getNumericValue(ch[i]));
        }
    }
}

控制台输出为:

B - 11
a - 10
l - 21
? - -1
z - 35
s - 28

-1表示在事件characters被触发之前出现了问题,不是吗?

My ErrorHandler:

package com.hw;

import java.io.PrintStream;

import org.xml.sax.ErrorHandler;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

class MyErrorHandler implements ErrorHandler {
    private PrintStream out;

    MyErrorHandler(PrintStream out) {
        this.out = out;
    }

    private String getParseExceptionInfo(SAXParseException spe) {
        String systemId = spe.getSystemId();

        if (systemId == null) {
            systemId = "null";
        }

        String info = "URI=" + systemId + " Line=" + spe.getLineNumber() + ": "
                + spe.getMessage();

        return info;
    }

    public void warning(SAXParseException spe) throws SAXException {
        out.println("Warning: " + getParseExceptionInfo(spe));
    }

    public void error(SAXParseException spe) throws SAXException {
        String message = "Error: " + getParseExceptionInfo(spe);
        throw new SAXException(message);
    }

    public void fatalError(SAXParseException spe) throws SAXException {
        String message = "Fatal Error: " + getParseExceptionInfo(spe);
        throw new SAXException(message);
    }

}

1 个答案:

答案 0 :(得分:3)

您肯定会遇到输出编码问题,即控制台或接收输出的任何内容都无法正确处理UTF-16(这是本机java编码)。

并且,您也被Characters#getNumericValue()方法欺骗,认为您有输入或解析器编码问题。 getNumericValue()尝试将字符解释为表示数字的内容,而不是实际的代码点值或任何其他内容。如文档所述,如果您给罗马数字五十,Ⅼ(U + 216C),该方法将打印50

尝试替换该行:

System.out.println(ch[i] + " - " + Character.getNumericValue(ch[i]));
        System.out.println(ch[i] + " - " + Character.getNumericValue(ch[i]));

System.out.println(ch[i] + " - " + Integer.toHexString((int) ch[i]));

你可能会看到它打印

? - e1

现在,如何修复输出编码问题:除非您提供更多详细信息,否则我无法帮助您。

<强>更新

您可以在

中设置eclipse控制台编码
Run Configurations --> Common

或使用

在JDK / JRE中
-Dfile.encoding

属性(不是100%肯定这个)。