xml验证

uubf1zoe  于 2021-07-03  发布在  Java
关注(0)|答案(1)|浏览(352)

目前,我参与了一个项目,其中包括验证xml文件。这些文件将由用户通过restapi上传,restapi是用java和spring框架编写的。架构文件位于xml文件[2]中的url[1]中,属性为“schemalocation”。xsd文件也可能包含多个其他模式文件。用户上载的文件是io链接设备描述(iodd)文件。
因此,我当前的问题是在主模式中加载包含的验证方案。我的目标不是下载模式并使用它们进行验证。整个过程必须是动态的。我也不想使用ioddchecker,它是由io-link本身提供的。
我了解到这可以通过resourcesolver接口完成,但是我找不到任何实现可以通过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;
}

这是schema receiver方法。它可以工作,但是当模式包含时,验证过程就失败了(此代码下面的错误消息。

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示例)

btqmn9zl

btqmn9zl1#

好吧,我自己解决了这个问题。简而言之,我必须创建一个实现接口的类 LSResourceResolver 并重写该方法 resolveResource .
答案很长:
首先我创建了类 XsdReceiver 并让它实现接口 LSResourceResolver . 还有方法 resolveResource 现在必须被覆盖。这个方法最终负责查询丢失的xsd文件并将它们作为资源返回 LSInput . (就像 LSResourceResolver , LSInput 是一个接口,因此必须作为一个单独的类实现。我只是叫它 Input ). 最后我用了这个方法 setResourceResolver 并示例化为创建的类的参数 XsdReceiver .
功能:
方法 isValid 应该检查xml模式的有效性。必要的模式文件可以在根标记中找到。它还必须手动传递给 SchemaFactory 班级。调用该方法并读取主模式文件后,每个引用的xml或xsd文件都会自动传递给该方法 resolveResource . 这个 SchemaFactory 全班为我们做这件事。
玩得开心: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;
    }
}

相关问题