使用 JAXB 验证架构

发布于 2024-08-26 02:03:49 字数 2818 浏览 9 评论 0原文

考虑到这听起来很容易,我一直在寻找这个问题的解决方案,所以我来寻求帮助。

我有一个 XML 架构,我已将其与 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>

...etc

我看到的每个示例都使用 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 来说太简单了。

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.

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.

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:

<?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.

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

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

对你的占有欲 2024-09-02 02:03:49

好的,我找到了解决方案。使用架构工厂创建架构,但不指定架构文件,使其可以与 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() 说:

对于 XML 模式,此方法创建一个
执行验证的架构对象
通过使用指定的位置提示
文件。

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

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.

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!

The javadoc for SchemaFactory.newSchema() says:

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.

暖心男生 2024-09-02 02:03:49

据我所知,您只需使用 Marshaller.setSchema() 到由 SchemaFactory 来自您的 DDSSettings.xsd。这将打开验证。

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文