xml 使用 JAXB 针对 Schema 进行验证

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2491859/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 13:02:24  来源:igfitidea点击:

Validating against a Schema with JAXB

xmlvalidationschemajaxbwell-formed

提问by fwgx

I've been looking for solutions to this problem for far too long considering how easy it sounds so I've come for some help.

考虑到这听起来很简单,我一直在寻找解决这个问题的方法太久了,所以我来寻求帮助。

I have an XML Schema which I have used with xjc to create my JAXB binding. This works fine when the XML is well formed. Unfortunately it also doesn't complain when the XML is not well formed. I cannot figure out how to do proper full validation against the schema when I try to unmarshall an XML file.

我有一个 XML 模式,我已经将它与 xjc 一起使用来创建我的 JAXB 绑定。当 XML 格式正确时,这可以正常工作。不幸的是,当 XML 格式不正确时,它也不会抱怨。当我尝试解组 XML 文件时,我无法弄清楚如何对架构进行正确的完整验证。

I have managed to use a ValidationEventCollector to handle events, which works for XML parsing errors such as mismatched tags but doesn't raise any events when there is a tag that is required but is completely absent.

我设法使用 ValidationEventCollector 来处理事件,它适用于 XML 解析错误,例如标签不匹配,但在需要但完全不存在的标签时不会引发任何事件。

From what I have seen validation can be done againsta schema, but you must know the path to the schema in order to pass it into the setSchema() method. The problem I have is that the path to the schema is stored in the XML header and I can't knwo at run time where the schema is going to be. Which is why it's stored in the XML file:

从我所见,验证可以针对模式进行,但您必须知道模式的路径才能将其传递给 setSchema() 方法。我遇到的问题是模式的路径存储在 XML 标头中,我无法在运行时知道模式的位置。这就是它存储在 XML 文件中的原因:

<?xml version="1.0" encoding="utf-8"?>
<DDSSettings xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="/a/big/long/path/to/a/schema/file/DDSSettings.xsd">
<Field1>1</Field1>
<Field2>-1</Field2>

...etc

...等等

Every example I see uses setValidating(true), which is now deprecated, so throws an exception.

我看到的每个示例都使用 setValidating(true),它现在已被弃用,因此会引发异常。

This is the Java code I have so far, which seems to only do XML validation, not schema validation:

这是我到目前为止的 Java 代码,它似乎只进行 XML 验证,而不是模式验证:

try {
    JAXBContext jc = new JAXBContext() {
        private final JAXBContext jaxbContext = JAXBContext.newInstance("blah");

        @Override
        public Unmarshaller createUnmarshaller() throws JAXBException {
            Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
            ValidationEventCollector vec = new ValidationEventCollector() {
                @Override
                public boolean handleEvent(ValidationEvent event) throws RuntimeException {
                    ValidationEventLocator vel = event.getLocator();
                    if (event.getSeverity() == event.ERROR || event.getSeverity() == event.FATAL_ERROR) {
                        String error = "XML Validation Exception:  " + event.getMessage() + " at row: " + vel.getLineNumber() + " column: " + vel.getColumnNumber();
                        System.out.println(error);
                    }
                    m_unmarshallingOk = false;
                    return false;
                }
            };
            unmarshaller.setEventHandler(vec);

            return unmarshaller;
        }

        @Override
        public Marshaller createMarshaller() throws JAXBException {
            throw new UnsupportedOperationException("Not supported yet.");
        }

        @Override
        @SuppressWarnings("deprecation")
        public Validator createValidator() throws JAXBException {
            throw new UnsupportedOperationException("Not supported yet.");
        }
    };

    Unmarshaller unmarshaller = jc.createUnmarshaller();
    m_ddsSettings = (com.ultra.DDSSettings)unmarshaller.unmarshal(new File(xmlfileName));
} catch (UnmarshalException ex) {
    Logger.getLogger(UniversalDomainParticipant.class.getName()).log(
    Level.SEVERE,
    null, ex);
} catch (JAXBException ex) {
    Logger.getLogger(UniversalDomainParticipant.class.getName()).log(
    Level.SEVERE,
    null, ex);
}

So what is the proper way to do this validation? I was expecting there to be a validate() method on the JAXB generated classes, but I guess that would be too simple for Java.

那么进行此验证的正确方法是什么?我期待在 JAXB 生成的类上有一个 validate() 方法,但我想这对 Java 来说太简单了。

回答by fwgx

OK, I've found the solution. Using the schema factory to create a schema, but without specifying a schema file makes it work with the noNamespaceSchemaLocation specified in the XML file.

好的,我找到了解决方案。使用架构工厂创建架构,但不指定架构文件,使其与 XML 文件中指定的 noNamespaceSchemaLocation 一起使用。

So the code from above has had this added:

所以上面的代码添加了这个:

SchemaFactory factory = SchemaFactory.newInstance("http://www.w3.org/2001/XMLSchema");
Schema schema = factory.newSchema();
Unmarshaller unmarshaller = jc.createUnmarshaller();
unmarshaller.setSchema(schema);
m_ddsSettings = (com.ultra.DDSSettings)unmarshaller.unmarshal(new File(xmlfileName));

Shame that took the best part of 24 hours to find the answer to!

耻辱花了 24 小时才找到答案!

The javadoc for SchemaFactory.newSchema()says:

javadoc forSchemaFactory.newSchema()说:

For XML Schema, this method creates a Schema object that performs validation by using location hints specified in documents.

The returned Schema object assumes that if documents refer to the same URL in the schema location hints, they will always resolve to the same schema document. This asusmption allows implementations to reuse parsed results of schema documents so that multiple validations against the same schema will run faster.

对于 XML Schema,此方法创建一个 Schema 对象,该对象通过使用文档中指定的位置提示来执行验证。

返回的 Schema 对象假定如果文档在架构位置提示中引用相同的 URL,它们将始终解析为相同的架构文档。这种假设允许实现重用模式文档的解析结果,以便针对同一模式的多个验证运行得更快。

回答by Arne Burmeister

As far as I know, you just have to set the schema with Marshaller.setSchema()to a schema created by the SchemaFactoryfrom your DDSSettings.xsd. This will turn validation on.

据我所知,您只需要使用Marshaller.setSchema()将架构设置为SchemaFactory从您的DDSSettings.xsd. 这将打开验证。