【问题标题】:Stream closed by resolving resource通过解析资源关闭流
【发布时间】:2016-04-28 13:13:55
【问题描述】:

我正在尝试为模式实现一个 xsd 验证器,它使用导入和包含。我以this answer 为例。

这是我的验证方法:

public void validate(String filePath, String schemaName) throws Exception
{
    SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
    factory.setResourceResolver(new ResourceResolver());
    Source schemaFile = new StreamSource(getClass().getClassLoader().getResourceAsStream(schemaName));
    Schema schema = factory.newSchema(schemaFile);
    Validator validator = schema.newValidator();
    validator.setErrorHandler(new MySAXParseErrorHandler());
    validator.validate(new StreamSource(filePath));
}

LSInput(完全一样):

public class LSInputImpl implements LSInput
{

private String publicId;

private String systemId;

public String getPublicId()
{
    return publicId;
}

public void setPublicId(String publicId)
{
    this.publicId = publicId;
}

public String getBaseURI()
{
    return null;
}

public InputStream getByteStream()
{
    return null;
}

public boolean getCertifiedText()
{
    return false;
}

public Reader getCharacterStream()
{
    return null;
}

public String getEncoding()
{
    return null;
}

public String getStringData()
{
    synchronized (inputStream) {
        try {
            byte[] input = new byte[inputStream.available()];
            inputStream.read(input);
            String contents = new String(input);
            return contents;
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("Exception " + e);
        }
        return null;
    }
}

public void setBaseURI(String baseURI)
{
}

public void setByteStream(InputStream byteStream)
{
}

public void setCertifiedText(boolean certifiedText)
{
}

public void setCharacterStream(Reader characterStream)
{
}

public void setEncoding(String encoding)
{
}

public void setStringData(String stringData)
{
}

public String getSystemId()
{
    return systemId;
}

public void setSystemId(String systemId)
{
    this.systemId = systemId;
}

public BufferedInputStream getInputStream()
{
    return inputStream;
}

public void setInputStream(BufferedInputStream inputStream)
{
    this.inputStream = inputStream;
}

private BufferedInputStream inputStream;

public LSInputImpl(String publicId, String sysId, InputStream input)
{
    this.publicId = publicId;
    this.systemId = sysId;
    this.inputStream = new BufferedInputStream(input);
}

}

资源解析器:

public class ResourceResolver  implements LSResourceResolver
{

   public LSInput resolveResource(String type, String namespaceURI,
                           String publicId, String systemId, String baseURI)       {


    InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(systemId);       
    return new LSInputImpl(publicId, systemId, resourceAsStream);
}

}

当我尝试使用只有导入的 xsd 验证 xml 时,它工作正常。但是,当我使用 include 将一个模式与两个模式拆分时,进程会因 IOException 而失败:

java.io.IOException: Stream closed
at java.io.BufferedInputStream.getInIfOpen(BufferedInputStream.java:159)
at java.io.BufferedInputStream.available(BufferedInputStream.java:410)
at LSInputImpl.getStringData(LSInputImpl.java:57)
at com.sun.org.apache.xerces.internal.util.DOMEntityResolverWrapper.resolveEntity(DOMEntityResolverWrapper.java:130)
at com.sun.org.apache.xerces.internal.impl.XMLEntityManager.resolveEntity(XMLEntityManager.java:1073)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.resolveDocument(XMLSchemaLoader.java:659)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.resolveSchemaSource(XSDHandler.java:2105)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.constructTrees(XSDHandler.java:1088)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.constructTrees(XSDHandler.java:1120)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.parseSchema(XSDHandler.java:620)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadSchema(XMLSchemaLoader.java:616)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:574)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:540)
at com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory.newSchema(XMLSchemaFactory.java:255)
at javax.xml.validation.SchemaFactory.newSchema(SchemaFactory.java:638)
at Main.validate(Main.java:54)
at Main.main(Main.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

org.xml.sax.SAXParseException; systemId: file:///C:/Users/adobryn/Java/sub/schema.xsd; lineNumber: 456; columnNumber: 73; src-resolve: Cannot resolve the name 'st_Term' to a(n) 'type definition' component.
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.createSAXParseException(ErrorHandlerWrapper.java:203)
at com.sun.org.apache.xerces.internal.util.ErrorHandlerWrapper.error(ErrorHandlerWrapper.java:134)
at com.sun.org.apache.xerces.internal.impl.XMLErrorReporter.reportError(XMLErrorReporter.java:437)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaErr(XSDHandler.java:4162)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.reportSchemaError(XSDHandler.java:4145)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.getGlobalDecl(XSDHandler.java:1741)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDElementTraverser.traverseNamedElement(XSDElementTraverser.java:405)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDElementTraverser.traverseLocal(XSDElementTraverser.java:194)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.traverseLocalElements(XSDHandler.java:3618)
at com.sun.org.apache.xerces.internal.impl.xs.traversers.XSDHandler.parseSchema(XSDHandler.java:633)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadSchema(XMLSchemaLoader.java:616)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:574)
at com.sun.org.apache.xerces.internal.impl.xs.XMLSchemaLoader.loadGrammar(XMLSchemaLoader.java:540)
at com.sun.org.apache.xerces.internal.jaxp.validation.XMLSchemaFactory.newSchema(XMLSchemaFactory.java:255)
at javax.xml.validation.SchemaFactory.newSchema(SchemaFactory.java:638)
at Main.validate(Main.java:54)
at Main.main(Main.java:68)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144)

包含包含的架构:

<?xml version="1.0" encoding="UTF-8"?>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.w3.org/schema" targetNamespace="http://www.w3.org/schema" elementFormDefault="qualified">    
    <xsd:include schemaLocation="st_Term.xsd"/>
    <xsd:element name="Term" type="st_Term" minOccurs="0" maxOccurs="1"/>
</xsd:schema>

我认为,getStringData() 方法有问题。我该如何修改它?感谢您的帮助!

【问题讨论】:

  • Javadoc 中特别警告您不要使用InputStream.available(),而您忽略了InputStream.read() 的结果,它可能只有1,甚至-1。

标签: java xml validation xsd


【解决方案1】:

问题是,包含的 xsd 也有一个包含,并且 ResourceResolver 在资源文件夹中查找它,而不是在该子包含的 xsd 所在的资源/子目录中。所以,我举了另一个例子,它有path tracking,它现在可以工作了:) 请注意,schemaBasePath 应该以“/”开头

【讨论】:

    【解决方案2】:

    几年前我在 .xsd 中包含同样的问题。

    我记得我的问题是 .xsd 资源及其包含位于资源中的某个包(目录)中。

    我已经使用以下 ResourceResolver 解决了这个问题,其中包被传递给构造函数(resourceRoot 参数):

    class ResourceResolver implements LSResourceResolver {
    
        private String resourceRoot;
    
        public ResourceResolver(String resourceRoot) {
            this.resourceRoot = resourceRoot;
        }
    
        public LSInput resolveResource(String type, String namespaceURI,String publicId, String systemId, String baseURI) {
            InputStream resourceAsStream = this.getClass().getResourceAsStream(resourceRoot + "/" + systemId);
            return new Input(publicId, systemId, resourceAsStream);
        }
    
    }
    

    和输入:

    static class Input implements LSInput {
    
        private String publicId;
    
        private String systemId;
    
        public String getPublicId() {
            return publicId;
        }
    
        public void setPublicId(String publicId) {
            this.publicId = publicId;
        }
    
        public String getBaseURI() {
            return null;
        }
    
        public InputStream getByteStream() {
            return null;
        }
    
        public boolean getCertifiedText() {
            return false;
        }
    
        public Reader getCharacterStream() {
            return null;
        }
    
        public String getEncoding() {
            return null;
        }
    
        public String getStringData() {
            synchronized (inputStream) {
                try {
                    byte[] input = new byte[inputStream.available()];
                    inputStream.read(input);
                    String contents = new String(input, "UTF-8");
                    return contents;
                } catch (IOException e) {
                    e.printStackTrace();
                    System.out.println("Exception " + e);
                    return null;
                }
            }
        }
    
        public void setBaseURI(String baseURI) {
        }
    
        public void setByteStream(InputStream byteStream) {
        }
    
        public void setCertifiedText(boolean certifiedText) {
        }
    
        public void setCharacterStream(Reader characterStream) {
        }
    
        public void setEncoding(String encoding) {
        }
    
        public void setStringData(String stringData) {
        }
    
        public String getSystemId() {
            return systemId;
        }
    
        public void setSystemId(String systemId) {
            this.systemId = systemId;
        }
    
        private final BufferedInputStream inputStream;
    
        public Input(String publicId, String sysId, InputStream input) {
            this.publicId = publicId;
            this.systemId = sysId;
            this.inputStream = new BufferedInputStream(input);
        }
    }
    

    【讨论】:

    • Javadoc 中特别警告您不要使用 InputStream.available(),而您忽略了 InputStream.read() 的结果,它可能只有 1,或者实际上是 -1。
    猜你喜欢
    • 2011-01-08
    • 1970-01-01
    • 2010-09-22
    • 1970-01-01
    • 2021-11-26
    • 1970-01-01
    • 2016-01-19
    • 2016-10-19
    • 1970-01-01
    相关资源
    最近更新 更多