【问题标题】:XML Validation with non local schemas使用非本地模式进行 XML 验证
【发布时间】:2021-03-12 11:03:55
【问题描述】:

目前我参与了一个项目,其中我必须验证 XML 文件。这些文件将由用户通过使用 Spring 框架用 Java 编写的 REST API 上传。模式文件位于 XML 文件 [2] 中的 URL[1] 作为属性“schemaLocation”。 XSD 文件也可能包含多个其他模式文件。用户上传的文件是 IO-Link 设备描述 (IODD) 文件。

所以,我当前的问题是在主架构中加载包含的验证方案。下载模式并使用它们进行验证不是我的目标。整个过程必须是动态的。我也不想使用 IO-Link 本身提供的 IODDChecker。

我读到这可以通过 ResourceResolver 接口完成,但我找不到任何实现来通过 URL 或类似的方式从主架构加载包含的架构。

那么,你能帮我找到解决这个问题的方法吗?

提前谢谢你!


这是验证文件的方法:

public boolean isValid(String file) {
    if (file == null || file.isEmpty() || !Files.exists(Path.of(file)) || !Files.isReadable(Path.of(file)))
        return false;
    else if (this.getStamp() == null || this.getStamp().getChecker() == null)
        return false;
    else if (this.getStamp().getCrc().isEmpty())
        return false;

    try {
        SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);

        factory.setErrorHandler(new LineNumberErrorHandler());

        Schema schema = factory.newSchema(XsdReceiver.receive(this.schemaLocation));
        Validator validator = schema.newValidator();
        //validator.validate(new StreamSource(new ByteArrayInputStream(data)));

        validator.setResourceResolver(factory.getResourceResolver());
        validator.validate(new StreamSource(new File(file)));
    } catch (Exception e) {
        return false;
    }

    return true;
}

这是模式接收器方法。它可以工作,但是当架构包含时,验证过程就会失败。 (此代码下方的错误消息。

public static Source receive(String url) {
    url = url.contains(" ") ? url.replace(" ", "/") : url;

    try {
        URL u = new URL(url);
        HttpURLConnection c = (HttpURLConnection)u.openConnection();
        int status = c.getResponseCode();

        if (status == HttpURLConnection.HTTP_MOVED_TEMP
                || status == HttpURLConnection.HTTP_MOVED_PERM
                || status == HttpURLConnection.HTTP_SEE_OTHER
        )
            c = (HttpURLConnection) new URL(c.getHeaderField("Location")).openConnection();


        return new StreamSource(c.getInputStream());
    } catch (IOException e) {
        e.printStackTrace();
    }

    return null;
}

我从验证器收到的错误消息。

Line: 3) schema_reference.4: Failed to read schema document 'IODD-Primitives1.1.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.
Line: 4) schema_reference.4: Failed to read schema document 'IODD-Datatypes1.1.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.
Line: 5) schema_reference.4: Failed to read schema document 'IODD-Variables1.1.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.
Line: 6) schema_reference.4: Failed to read schema document 'IODD-Events1.1.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.
Line: 7) schema_reference.4: Failed to read schema document 'IODD-UserInterface1.1.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.
Line: 8) schema_reference.4: Failed to read schema document 'IODD-Communication1.1.xsd', because 1) could not find the document; 2) the document could not be read; 3) the root element of the document is not <xsd:schema>.
Line: 180) src-resolve: Cannot resolve the name 'DeviceIdT' to a(n) 'type definition' component.
Line: 180) src-resolve: Cannot resolve the name 'DeviceIdT' to a(n) 'simpleType definition' component.
Line: 191) src-resolve: Cannot resolve the name 'DeviceIdT' to a(n) 'type definition' component.
Line: 228) src-resolve: Cannot resolve the name 'CollectionT' to a(n) 'type definition' component.
Line: 292) src-resolve: Cannot resolve the name 'ObjectT' to a(n) 'type definition' component.
Line: 303) src-resolve: Cannot resolve the name 'CollectionT' to a(n) 'type definition' component.
Line: 312) src-resolve: Cannot resolve the name 'DataItemT' to a(n) 'type definition' component.
Line: 12) src-resolve: Cannot resolve the name 'DocumentInfoT' to a(n) 'type definition' component.
Line: 15) src-resolve: Cannot resolve the name 'CommNetworkProfileT' to a(n) 'type definition' component.
Line: 16) src-resolve: Cannot resolve the name 'ExternalTextCollectionT' to a(n) 'type definition' component.
Line: 22) src-resolve: Cannot resolve the name 'StampT' to a(n) 'type definition' component.
Line: 152) src-resolve: Cannot resolve the name 'TextRefT' to a(n) 'type definition' component.
Line: 153) src-resolve: Cannot resolve the name 'TextRefT' to a(n) 'type definition' component.
Line: 168) src-resolve: Cannot resolve the name 'TextRefT' to a(n) 'type definition' component.
Line: 169) src-resolve: Cannot resolve the name 'TextRefT' to a(n) 'type definition' component.
Line: 195) src-resolve: Cannot resolve the name 'TextRefT' to a(n) 'type definition' component.
Line: 196) src-resolve: Cannot resolve the name 'TextRefT' to a(n) 'type definition' component.
Line: 238) src-resolve: Cannot resolve the name 'DatatypeCollectionT' to a(n) 'type definition' component.
Line: 239) src-resolve: Cannot resolve the name 'VariableCollectionT' to a(n) 'type definition' component.
Line: 250) src-resolve: Cannot resolve the name 'ErrorTypeCollectionT' to a(n) 'type definition' component.
Line: 257) src-resolve: Cannot resolve the name 'EventCollectionT' to a(n) 'type definition' component.
Line: 263) src-resolve: Cannot resolve the name 'UserInterfaceT' to a(n) 'type definition' component.

[1]https://www.io-link.com/IODD/2010/10/IODD1.1.xsd
[2]https://ioddfinder.io-link.com/productvariants/search/11765(以 IO-Link 产品 TV7105 为例)

【问题讨论】:

    标签: java xml validation xsd


    【解决方案1】:

    好的,我自己解决了这个问题。简而言之,我必须创建一个实现接口LSResourceResolver 并覆盖方法resolveResource 的类。

    长答案是:
    首先,我创建了类XsdReceiver,并让它实现了接口LSResourceResolver。现在还必须覆盖方法resolveResource。该方法最终负责查询丢失的 XSD 文件并将它们作为资源 LSInput 返回。 (就像LSResourceResolverLSInput 是一个接口,因此必须作为一个单独的类来实现。我只是简单地称它为Input)。最后我使用了setResourceResolver的方法,并将创建的类XsdReceiver作为参数实例化。

    功能:
    方法isValid 应该检查XML 模式的有效性等。必要的模式文件可以在根标签中找到。它还必须手动传递给SchemaFactory 类。在调用该方法并读取主模式文件后,每个引用的 XML 或 XSD 文件都会自动传递给方法resolveResourceSchemaFactory 类为我们完成了这项工作。

    玩得开心:D


    public boolean isValid(byte[] data) {
        if (this.getStamp() == null || this.getStamp().getChecker() == null)
            return false;
        else if (this.getStamp().getCrc().isEmpty())
            return false;
    
        try {
            SchemaFactory factory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
    
            factory.setErrorHandler(new LineNumberErrorHandler());
            factory.setResourceResolver(new XsdReceiver());
    
            Schema schema = factory.newSchema(XsdReceiver.receive(this.schemaLocation));
            Validator validator = schema.newValidator();
    
            validator.setResourceResolver(factory.getResourceResolver());
            validator.validate(new StreamSource(new ByteArrayInputStream(data)));
        } catch (Exception e) {
            e.printStackTrace();
            return false;
        }
    
        return true;
    }
    
    public class XsdReceiver implements LSResourceResolver {
        public static Source receive(String url) {
            url = url.contains(" ") ? url.replace(" ", "/") : url;
            HttpURLConnection c = XsdReceiver.followRedirection(url);
    
            if (c == null)
                return null;
    
            try {
                return new StreamSource(c.getInputStream());
            } catch (IOException e) {
                return null;
            }
        }
    
        private static HttpURLConnection followRedirection(String url) {
            HttpURLConnection c = null;
    
            try {
                URL u = new URL(url);
                c = (HttpURLConnection)u.openConnection();
                int status = c.getResponseCode();
    
                if (status == HttpURLConnection.HTTP_MOVED_TEMP
                        || status == HttpURLConnection.HTTP_MOVED_PERM
                        || status == HttpURLConnection.HTTP_SEE_OTHER
                )
                    c = (HttpURLConnection) new URL(c.getHeaderField("Location")).openConnection();
            } catch (IOException e) {
                return null;
            }
    
            return c;
        }
    
        @Override
        public LSInput resolveResource(String type, String namespaceURI, String publicId, String systemId, String baseURI) {
            if (namespaceURI == null || type.equals("http://www.w3.org/TR/REC-xml"))
                return null;
    
            if (systemId.equals("xml.xsd")) {
                try {
                    File f = ResourceUtils.getFile("classpath:data/xml.xsd");
    
                    return new Input(StandardCharsets.UTF_8.name(), new FileInputStream(f), null, systemId, publicId, namespaceURI, true, null);
                } catch (IOException e) {
                    return null;
                }
            }
    
            String file = String.join("/", namespaceURI, systemId);
            HttpURLConnection connection;
            Input i = null;
    
            connection = XsdReceiver.followRedirection(file);
    
            if (connection == null)
                return null;
    
            try {
                i = new Input(StandardCharsets.UTF_8.name(), connection.getInputStream(), null, systemId, publicId, namespaceURI, true, null);
            } catch (IOException e) {
                return null;
            }
    
            return i;
        }
    }
    
    public class Input implements LSInput {
        private String encoding;
        private InputStream byteStream;
        private String stringData;
        private String systemId;
        private String publicId;
        private String baseUri;
        boolean certifiedText;
        private Reader characterStream;
    
        public Input(String encoding, InputStream byteStream, String stringData, String systemId, String publicId, String baseUri, boolean certifiedText, Reader characterStream) {
            this.encoding = encoding;
            this.byteStream = byteStream;
            this.stringData = stringData;
            this.systemId = systemId;
            this.publicId = publicId;
            this.baseUri = baseUri;
            this.certifiedText = certifiedText;
            this.characterStream = characterStream;
        }
    
        @Override
        public Reader getCharacterStream() {
            return this.characterStream;
        }
    
        @Override
        public void setCharacterStream(Reader characterStream) {
            this.characterStream = characterStream;
        }
    
        @Override
        public InputStream getByteStream() {
            return this.byteStream;
        }
    
        @Override
        public void setByteStream(InputStream byteStream) {
            this.byteStream = byteStream;
        }
    
        @Override
        public String getStringData() {
            return this.stringData;
        }
    
        @Override
        public void setStringData(String stringData) {
            this.stringData = stringData;
        }
    
        @Override
        public String getSystemId() {
            return this.systemId;
        }
    
        @Override
        public void setSystemId(String systemId) {
            this.systemId = systemId;
        }
    
        @Override
        public String getPublicId() {
            return this.publicId;
        }
    
        @Override
        public void setPublicId(String publicId) {
            this.publicId = publicId;
        }
    
        @Override
        public String getBaseURI() {
            return this.baseUri;
        }
    
        @Override
        public void setBaseURI(String baseURI) {
            this.baseUri = baseURI;
        }
    
        @Override
        public String getEncoding() {
            return this.encoding;
        }
    
        @Override
        public void setEncoding(String encoding) {
            this.encoding = encoding;
        }
    
        @Override
        public boolean getCertifiedText() {
            return this.certifiedText;
        }
    
        @Override
        public void setCertifiedText(boolean certifiedText) {
            this.certifiedText = certifiedText;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-04-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多