【问题标题】:Jaxb2Marshaller schema validation doesn't seem to be workingJaxb2Marshaller 模式验证似乎不起作用
【发布时间】:2014-01-13 22:47:00
【问题描述】:

我发现 Jaxb2Marshaller 在进行解组时未能针对 XSD 验证我的无效 XML。 我正在使用 Spring 4.0.0.RELEASE 和 Java 7。

这是一个例子:

XSD 文件:fruit.xsd

<?xml version="1.0" encoding="UTF-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
    elementFormDefault="qualified">

    <xs:attribute name="quantity" type="xs:string" />

    <xs:element name="fruit">
        <xs:complexType>
            <xs:sequence>
                <xs:choice>
                    <xs:element ref="banana" />
                    <xs:element ref="apple" />
                </xs:choice>
            </xs:sequence>
        </xs:complexType>
    </xs:element>

    <xs:element name="banana">
        <xs:complexType>
            <xs:attribute ref="quantity" use="required" />
        </xs:complexType>
    </xs:element>

    <xs:element name="apple">
        <xs:complexType>
            <xs:attribute ref="quantity" use="required" />
        </xs:complexType>
    </xs:element>

</xs:schema>

我使用 Spring Tool Suite 从这个 XSD 生成 JAXB POJO 到 com.testraptor.xml.jaxb 包。

我的无效 XML 文件:invalid.xml

<?xml version="1.0" encoding="UTF-8"?>
<fruit>
    <banana quantity="5" />
    <apple quantity="3" />
</fruit>

如您所见,我破坏了架构,因为水果标签中有一个选择,我同时使用了香蕉和苹果。

我的 Spring 配置文件:app-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:oxm="http://www.springframework.org/schema/oxm"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
    http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
    http://www.springframework.org/schema/oxm 
    http://www.springframework.org/schema/oxm/spring-oxm-4.0.xsd">

    <oxm:jaxb2-marshaller id="marshaller" context-path="com.fruit.xml.jaxb" />

</beans>

要测试的主类:Main.java

package com.fruit.test;

import java.io.IOException;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import javax.xml.validation.SchemaFactory;

import org.springframework.oxm.jaxb.Jaxb2Marshaller;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.core.io.Resource;
import org.xml.sax.SAXException;

import com.fruit.xml.jaxb.Fruit;

public class Main {

    public static void main(String[] args) {
        ApplicationContext appContext = new ClassPathXmlApplicationContext(
                "app-context.xml");

        //--------------------------------SCHEMA VALIDATION IS OMITTED

        Jaxb2Marshaller jaxb2Unmarshaller = (Jaxb2Marshaller) appContext
                .getBean("marshaller");
        Resource schemaResource = appContext
                .getResource("classpath:fruit.xsd");
        jaxb2Unmarshaller.setSchema(schemaResource);

        Resource xml = appContext.getResource("classpath:invalid.xml");

        try {
            Fruit fruit = (Fruit) jaxb2Unmarshaller.unmarshal(new StreamSource(xml.getInputStream()));
            System.out.println("SCHEMA VALIDATION IS OMITTED:");
            System.out.println("Apple quantity is " + fruit.getApple().getQuantity());
            System.out.println("Banana quantity is " + fruit.getBanana().getQuantity());
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }

        //--------------------------------SCHEMA VALIDATION IS PASSED
        SchemaFactory sf = SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI);
        Schema schema;
        try {
            schema = sf.newSchema(schemaResource.getURL());
            JAXBContext context = JAXBContext.newInstance(Fruit.class);
            Unmarshaller unmarshaller = (Unmarshaller) context.createUnmarshaller(); 
            unmarshaller.setSchema(schema); 
            Fruit fruit2 = (Fruit) unmarshaller.unmarshal(xml.getInputStream());
            System.out.println("SCHEMA VALIDATION IS PASSED:");
            System.out.println("Apple quantity is " + fruit2.getApple().getQuantity());
            System.out.println("Banana quantity is " + fruit2.getBanana().getQuantity());
        } catch (SAXException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (IOException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } catch (JAXBException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        } 
    }

}

当我运行代码时,我得到以下结果:

INFO: Loading XML bean definitions from class path resource [app-context.xml]
dec. 26, 2013 9:33:22 DE org.springframework.oxm.jaxb.Jaxb2Marshaller createJaxbContextFromContextPath
INFO: Creating JAXBContext with context path [com.fruit.xml.jaxb]
SCHEMA VALIDATION IS OMITTED:
Apple quantity is 3
Banana quantity is 5
javax.xml.bind.UnmarshalException
 - with linked exception:
[org.xml.sax.SAXParseException; lineNumber: 4; columnNumber: 24; cvc-complex-type.2.4.d: Invalid content was found starting with element 'apple'. No child element is expected at this point.]
    at ...

正如您所见,Jaxb2Marshaller 忘记验证我的无效 XML 文件,而 Unmarshaller 可以验证它。

谁能告诉我我的代码有什么问题?

【问题讨论】:

  • 托拜厄斯的回答对我有用。如果它也适合你,你能把它标记为正确的解决方案吗?

标签: java xml spring jaxb xsd


【解决方案1】:

我在运行时配置Jaxb2Marshaller 时遇到了同样的问题。通过 Spring XML 设置架构的建议解决方案让我思考:我的代码设置属性和 Spring 有什么区别?

答案很简单:Jaxb2Marshaller 实现了org.springframework.beans.factory.InitializingBean,它定义了生命周期方法afterPropertiesSet(),该方法在属性设置后由Springs BeanFactory 调用。

所以解决方案是在使用setSchema() 设置架构属性后调用afterPropertiesSet()。因为在该方法中,架构属性(org.springframework.core.io.Resource 数组)实际上用于设置 javax.xml.validation.Schema 类型的内部架构字段,稍后用于架构验证。

示例:

Jaxb2Marshaller marshaller = new Jaxb2Marshaller();
Resource schema = new ClassPathResource("fruit.xsd");
marshaller.setSchema(schema);
marshaller.setContextPath("com.fruit.xml.jaxb");
// manually call Spring lifecycle method which actually loads the schema resource
marshaller.afterPropertiesSet();
// use marshaller...

【讨论】:

  • 谢谢!!我到处搜索,这是唯一可以找到确切上下文正确答案的地方(使用 Spring、Spring 的 Jaxb2 编组器和模式文件进行模式验证)
【解决方案2】:

尝试通过调用 setValidationEventHandler() 设置一个validationEventHandler,看看它是否被调用?

setValidationEventHandler(new ValidationEventHandler() {
                @Override
                public boolean handleEvent(ValidationEvent event) {
                     System.out.println(event.getLinkedException().getMessage());
                    return false;
                }
            });)

【讨论】:

  • 感谢您的回复。我试过了,但不幸的是,添加 ValidationEventHandler 并没有解决问题,而且 Jaxb2Marshaller 似乎忘记调用 hanleEvent() 方法,因为我在控制台上看不到它的 sysout 结果。
  • 不确定是否会有所帮助,但您可以尝试将架构直接设置到 xml 配置中 而不是 jaxb2Unmarshaller.setSchema(schemaResource);
  • 不能在 oxm:jaxb2-marshaller bean 定义中使用“property”元素,因为我收到以下错误:发现以元素“property”开头的内容无效。应为 '{"springframework.org/schema/oxm":class-to-be-bound}' 之一。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-03-12
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-12
  • 2018-07-30
相关资源
最近更新 更多