xml – 使用DTD的相对路径解组文档时的JAXB SAXParseException

我有一个类从第三方源解组xml(我无法控制内容).这是解组的片段:

JAXBContext jContext = JAXBContext.newInstance("com.optimumlightpath.it.aspenoss.xsd"); 
Unmarshaller unmarshaller = jContext.createUnmarshaller() ;
StringReader xmlStr = new StringReader(str.value);
Connections conns = (Connections) unmarshaller.unmarshal(xmlStr); 

Connections是使用xjc生成的类dtd-> xsd->类. com.optimumlightpath.it.aspenoss.xsd包中包含所有这些类.

我收到的xml包含DOCTYPE中的相对路径.基本上str.value包含:

<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE Connections SYSTEM "./dtd/Connections.dtd">
<Connections>
...
</Connections>

这作为java 1.5应用程序成功运行.为了避免上述错误,我不得不在项目根目录下创建一个./dtd目录并包含所有dtd文件(不知道为什么我必须这样做但我们会这样做).

我已经在Tomcat5.5上创建了一个使用上述类的Web服务.我收到[org.xml.sax.SAXParseException:Relative URI“./dtd/Connections.dtd”;没有文档URI的情况下无法解析.]在unmarshal行上.我尝试在每个relavant文件夹(项目根目录,WebContent,WEB-INF,tomcat工作目录等)中创建./dtd无济于事.

问题1:我在哪里可以找到./dtd,以便在作为tomcat webservice运行时,类可以找到它?是否有任何tomcat或服务配置我需要做的才能识别目录?

问题2:为什么该类甚至首先需要dtd文件?它是否具有在dtd-> xsd->类的注释中解组所需的所有信息?我已经阅读了很多关于禁用验证,设置EntityResource和其他解决方案的帖子,但是这个类并不总是作为Web服务部署,我不想有两个代码序列.

最佳答案
当从InputStream或Reader解组时,解析器不知道文档的systemId(uri / location),因此它无法解析相对路径.似乎解析器尝试使用当前工作目录来解析引用,该目录仅在从ide或命令行运行时才有效.为了覆盖这种行为并自己解决,你需要实现一个EntityResolver,正如Blaise Doughan所提到的那样.

经过一些实验,我找到了一种标准的方法.您需要从SAXSource解组,而SAXSource又是从XMLReader和InputSource构造的.在此示例中,dtd位于带注释的类旁边,因此可以在类路径中找到.

Main.java

public class Main {
    private static final String FEATURE_NAMESPACES = "http://xml.org/sax/features/namespaces";
    private static final String FEATURE_NAMESPACE_PREFIXES = "http://xml.org/sax/features/namespace-prefixes";

    public static void main(String[] args) throws JAXBException, IOException, SAXException {
        JAXBContext ctx = JAXBContext.newInstance(Root.class);
        Unmarshaller unmarshaller = ctx.createUnmarshaller();

        XMLReader xmlreader = XMLReaderFactory.createXMLReader();
        xmlreader.setFeature(FEATURE_NAMESPACES, true);
        xmlreader.setFeature(FEATURE_NAMESPACE_PREFIXES, true);
        xmlreader.setEntityResolver(new EntityResolver() {
            public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
                // TODO: Check if systemId really references root.dtd
                return new InputSource(Root.class.getResourceAsStream("root.dtd"));
            }
        });

        String xml = "<!DOCTYPE root SYSTEM './root.dtd'><root><element>test</element></root>";
        InputSource input = new InputSource(new StringReader(xml));
        Source source = new SAXSource(xmlreader, input);

        Root root = (Root)unmarshaller.unmarshal(source);
        System.out.println(root.getElement());
    }
}

Root.java

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {
    @XmlElement
    private String element;

    public String getElement() {
        return element;
    }

    public void setElement(String element) {
        this.element = element;
    }
}

root.dtd

<?xml version="1.0" encoding="UTF-8"?>
<!ELEMENT root (element)>
<!ELEMENT element (#PCDATA)>

转载注明原文:xml – 使用DTD的相对路径解组文档时的JAXB SAXParseException - 代码日志