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

Posted

技术标签:

【中文标题】使用相同的输入流验证和读取【英文标题】:Validate AND read with the same inputstream 【发布时间】:2014-07-02 21:04:55 【问题描述】:

我有一个从 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)

【问题讨论】:

如果您从网络上阅读,我可以理解这种情况。部署在服务器中时,您还在从物理文件中读取 XML 吗? 我们通过 REST 服务获取 XML 文件作为字符串。我用来创建输入流的那个字符串。上面的示例仅用于演示行为。 请展示您从字符串创建 InputStream 的代码。请注意,将内容加载到任意长的字符串中通常不是一个好主意。首先将其复制到一个临时文件(可能使用java.nio.file.Files.copy(...))。 【参考方案1】:

不幸的是,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();
    

【讨论】:

谢谢。我为它使用了 NoCloseStream,它工作正常。【参考方案2】:

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

如果您评论 validateXML 行,是否一切正常:

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

如果有,您是否在 openEJB 类路径中的某处有一个不在 Glassfish 中的 xerces jar?

我很确定我过去遇到过这样的问题,但不记得确切的问题。

【讨论】:

【参考方案3】:

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

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

【讨论】:

这比完全消除BufferedInputStream 有什么好处?根据什么逻辑,这被认为比保留它更好?这些如何解决实际问题?【参考方案4】:

如果您不确定您的输入流是否被标记支持,您可以使用以下方法将其有效地复制到一个缓冲区,在验证后可以从中读取 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()));

【讨论】:

以上是关于使用相同的输入流验证和读取的主要内容,如果未能解决你的问题,请参考以下文章

字节输入流读取字节数据和字节输入流一次读取多个字节

使用重载输入流运算符读取带字符串的文件>>

java题 任选一个文件,使用文件输入流,将其数据读取出来,并打印到控制台!! 代码简单能够有注释

输入流与输出流

字节输入流_InputStream类&FilelnputStream类介绍和字节输入流读取字节数据

字节流和字符流