使用JAXB对架构进行验证

13

由于这个问题听起来很简单,我已经寻找解决方案很久了,现在来请求帮助。

我有一个 XML Schema,我使用 xjc 将其用于创建我的 JAXB 绑定。当 XML 是格式正确时,这个过程可以正常工作。但是,如果 XML 不是格式正确的,它也不会报错。当我尝试将 XML 文件反序列化时,我无法想出如何根据模式进行全面的验证。

我成功地使用 ValidationEventCollector 来处理事件。这对于 XML 解析错误(例如标签不匹配)有效,但是当存在必需但完全缺失的标签时,它不会引发任何事件。

据我所见,可以根据模式进行验证,但必须知道路径才能将其传递给 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>

我看到的每个示例都使用setValidating(true),但它现在已经被弃用,所以会抛出异常。

这是我目前的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);
}

那么正确的验证方式是什么?我原本期望JAXB生成的类有一个validate()方法,但我猜这对Java来说太简单了。

2个回答

16

好的,我已经找到了解决方案。使用模式工厂创建模式,但不指定模式文件,可以让它与XML文件中指定的noNamespaceSchemaLocation一起工作。

所以,以上代码已添加了以下内容:

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));

很遗憾,找到答案竟然耗费了整整24个小时!

SchemaFactory.newSchema()的Javadoc文档中写道:

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

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


有趣,我不知道那个。为了以后的参考,我冒昧地在你的回答中添加了一些信息。 - skaffman
2
不要直接指定XMLSchema的URL,根据javadocs,使用在此处指定的XMLConstants的适当值:http://docs.oracle.com/javase/6/docs/api/index.html?javax/xml/validation/Schema.html - icfantv
@icfantv,你说得对。我用的是:Schema schema = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI).newSchema(); // 但是参考文献是:http://docs.oracle.com/javase/6/docs/api/index.html?javax/xml/validation/Schema.html - 此解决方案中我没理解的是URL是常量:它并未指向我们自己模式的位置(对我来说不是很明显)。 - pdem
oops: 直接引用 XMLConstants:http://docs.oracle.com/javase/6/docs/api/javax/xml/XMLConstants.html#W3C_XML_SCHEMA_NS_URI - pdem

1

但问题是,在编译时我不知道那个XSD文件的路径,而且在运行时也没有给它 - 它仅存储在XML文件标题中。 - fwg
但是如果您有jaxb类,它们应该是从模式生成的。为什么不在您的项目中包含模式呢? - Arne Burmeister
JAXB不一定需要使用模式和生成的代码,它可以完美地在没有模式的情况下工作。 - skaffman

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接