使用相同的输入流验证和读取

时间:2014-05-14 17:06:01

标签: java xml validation xsd inputstream

我有一个web服务,它从xml文件中获取输入流。现在,我想要验证并使用相同的输入流读取它。我使用标记和重置。

在Glassfish和Websphere上它运行正常。但是当我使用openEJB运行集成测试时,流将在验证后关闭。我可以用一个简单的例子重现它。

我怎样才能更好地实现它?验证器实现始终是相同的。但是每个环境都使用另一种输入流实现。

public class XMLReader {
public static void main(String[] args) {
    try {
        XMLReader reader = new XMLReader();
        InputStream stream = new BufferedInputStream(new FileInputStream(
                new File("myXML.xml")));
        reader.read(stream);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

public void read(InputStream xmlInputStream) throws SAXException,
        IOException {
    if (xmlInputStream.markSupported()) {
        xmlInputStream.mark(0);
        validateXML(xmlInputStream);
        xmlInputStream.reset();
        readXML(xmlInputStream);
    }
}

private void readXML(InputStream xmlInputStream) {
    // READ xmInputStream with STAX, JAXB, etc. whatever

}

private void validateXML(InputStream xmlInputStream) throws SAXException,
        IOException {
    Source schemaFile = new StreamSource(new File("myXSD.xsd"));
    Source xmlFile = new StreamSource(xmlInputStream);
    SchemaFactory schemaFactory = SchemaFactory
            .newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
    Schema schema = schemaFactory.newSchema(schemaFile);
    Validator validator = schema.newValidator();
    try {
        validator.validate(xmlFile);
        System.out.println("is valid");
    } catch (SAXException e) {
        System.out.println("is NOT valid");
        System.out.println("Reason: " + e.getLocalizedMessage());
    }
}
}

例外:

  java.io.IOException: Stream closed
at java.io.BufferedInputStream.getBufIfOpen(BufferedInputStream.java:145)
at java.io.BufferedInputStream.reset(BufferedInputStream.java:414)
at xmltest.XMLReader.read(XMLReader.java:36)
at xmltest.XMLReader.main(XMLReader.java:27)

4 个答案:

答案 0 :(得分:4)

不幸的是,XML解析器在读完后关闭了流。我不明白为什么会这样做,我不建议任何人关闭他们不拥有的流。可能有一个原因我无法快速掌握这一点。

无论如何,你可以做的是拥有一个BufferedInputStream,它不会关闭包装的InputSteam:

public static void main(String[] args) {
    try (FileInputStream in = new FileInputStream(new File("myXML.xml"))){
        XMLReader reader = new XMLReader();
        InputStream stream = new BufferedInputStream(in) {
            @Override
            public void close() throws IOException {
               // don't close
            }
        };
        reader.read(stream);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

答案 1 :(得分:0)

这让我想到了一个覆盖SAX实现的东西,它在验证结束时关闭你的输入流。

如果您对validateXML行发表评论,那么一切正常吗?

if (xmlInputStream.markSupported()) {
    xmlInputStream.mark(0);
    // validateXML(xmlInputStream);
    xmlInputStream.reset();
    readXML(xmlInputStream);
}

如果是,你是否在openEJB类路径中的某个地方有一个xerces jar,它不是在Glassfish中?

我很确定我过去曾遇到这样的问题,但无法记住确切的问题。

答案 2 :(得分:0)

Marcel Steinbach解决方案的一个小修改:BufferedInputStream将维护一个缓冲区,这对于防止输入流关闭是不必要的。相反,您可以创建一个匿名的InputStream类,它只是将read方法委托给包含XML的可标记输入流。由于InputStream中close方法的默认实现不执行任何操作,因此流将在验证后保持打开状态:

validator.validate(new StreamSource(new InputStream() {
    @Override
    public int read() throws IOException {
        return xmlInputStream.read();
    }
}));

答案 3 :(得分:0)

如果您不确定您的输入流是否支持标记,您可以使用以下内容将其有效地复制到缓冲区中可以从中读取XML的缓冲区:

final StringWriter sw = new StringWriter(is.available());
validator.validate(new StreamSource(new InputStream() {
    @Override public int read() throws IOException {
        int ch = is.read();
        sw.write(ch);
        return ch;
    }
}));
readXML(new ByteArrayInputStream(sw.toString().getBytes()));