我有一个表示XML模式的POJO的对象树。这是使用以下jaxb ant脚本创建的。







我想针对缺少属性的模式验证根POJO及其子实体。

我的代码如下:(省略了try/catch块,受SO问题How to validate against schema in JAXB 2.0 without marshalling?的启发)

public boolean validateAgainstSchema(Pojo pojo)
{
        JAXBContext jc;
        jc = JAXBContext.newInstance(Pojo.class);
        SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        Schema schema = sf.newSchema(new ClassPathResource("schema.xsd").getFile());

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setSchema(schema);
        marshaller.marshal(schema, new DefaultHandler());
        return true;
}

我的属性之一(pojo.childEntity.someAttribute)是date
XSD
<xsd:attribute name="some_date" use="required">
  <xsd:simpleType>
    <xsd:restriction base="xsd:date" />
  </xsd:simpleType>
</xsd:attribute>

java
@XmlAttribute(name = "someDate", required = true)
protected XMLGregorianCalendar someDate;

它是从另一个POJO(与Hibernate映射的一个)的java.util.Date对象填充的。
private static final XMLGregorianCalendar dateToCalendar(Date date)
{
    if (date == null)
        return null;
    try
    {
        GregorianCalendar c = new GregorianCalendar();
        c.setTime(date);

        return DatatypeFactory.newInstance()
                .newXMLGregorianCalendar(c);
    }
    catch (DatatypeConfigurationException e)
    {
        e.printStackTrace();
        return null;
    }

}

异常(exception)是:
javax.xml.bind.MarshalException
 - with linked exception:
[org.xml.sax.SAXParseException: cvc-datatype-valid.1.2.1: '2001-05-11T00:00:00.000+02:00' is not a valid value for 'date'.]

看起来JAXB尝试为必须仅携带日期的字段设置日期和时间,而XMLGregorianCalendard只是日期时间容器。

问题是:什么原因导致错误?怎么修?

最佳答案

默认情况下,XMLGregorianCalendar属性的输出将基于您创建它的方式。如果您填充时间部分,那么该时间部分将被输出到XML。您可以调用getXMLSchemaType()方法来查看相应的XML表示是什么:

  • http://docs.oracle.com/javase/7/docs/api/javax/xml/datatype/XMLGregorianCalendar.html#getXMLSchemaType%28%29

  • 您可以使用@XmlSchemaType批注覆盖该表示形式。
  • http://docs.oracle.com/javase/7/docs/api/javax/xml/bind/annotation/XmlSchemaType.html

  • Java模型(根)

    以下是具有3个XMLGregorianCalendar字段的对象。在3号,我将使用@XmlSchemaType批注指定模式类型。
    import javax.xml.bind.annotation.*;
    import javax.xml.datatype.XMLGregorianCalendar;
    
    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Root {
    
        protected XMLGregorianCalendar default1;
    
        protected XMLGregorianCalendar default2;
    
        @XmlSchemaType(name="date")
        protected XMLGregorianCalendar schemaTypeDate;
    
    }
    

    演示代码

    在下面的演示代码中,我们将创建2个XMLGregorianCalendar实例。一个将具有模式类型date,另一个将具有模式类型dateTime。默认情况下,这是编码为XML时使用的XML表示形式。在schemaTypeDate字段上,我们将使用@XmlSchemaType批注覆盖默认值。
    import javax.xml.bind.*;
    import javax.xml.datatype.*;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            JAXBContext jc = JAXBContext.newInstance(Root.class);
    
            DatatypeFactory df = DatatypeFactory.newInstance();
            XMLGregorianCalendar date  = df.newXMLGregorianCalendar("2013-07-03");
            XMLGregorianCalendar dateTime = df.newXMLGregorianCalendar("1999-12-31T23:59:00");
    
            Root root = new Root();
            root.default1 = date;
            root.default2 = dateTime;
            root.schemaTypeDate = dateTime;
    
            Marshaller marshaller = jc.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.marshal(root, System.out);
        }
    
    }
    

    输出

    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <root>
        <default1>2013-07-03</default1>
        <default2>1999-12-31T23:59:00</default2>
        <schemaTypeDate>1999-12-31</schemaTypeDate>
    </root>
    

    更新



    当模式类型是XML Schema类型中的构建之一(即xsd:datexsd:dateTime)时,JAXB将为您执行此操作,但是当您扩展了其中一种类型时,JAXB将为您执行此操作。

    XML模式(schema.xsd)

    <?xml version="1.0" encoding="UTF-8"?>
    <schema xmlns="http://www.w3.org/2001/XMLSchema">
        <complexType name="root">
            <sequence>
                <element name="dateElement" type="date" />
                <element name="dateTimeElement" type="dateTime" />
                <element name="extendedDateElement">
                    <simpleType>
                        <restriction base="date" />
                    </simpleType>
                </element>
            </sequence>
            <attribute name="dateAttribute" type="date" />
            <attribute name="dateTimeAttribute" type="dateTime" />
            <attribute name="extendedDateAttribute">
                <simpleType>
                    <restriction base="date" />
                </simpleType>
            </attribute>
        </complexType>
    </schema>
    


    @XmlAccessorType(XmlAccessType.FIELD)
    @XmlType(name = "root", propOrder = {
        "dateElement",
        "dateTimeElement",
        "extendedDateElement"
    })
    public class Root {
    
        @XmlElement(required = true)
        @XmlSchemaType(name = "date")
        protected XMLGregorianCalendar dateElement;
    
        @XmlElement(required = true)
        @XmlSchemaType(name = "dateTime")
        protected XMLGregorianCalendar dateTimeElement;
    
        @XmlElement(required = true)
        protected XMLGregorianCalendar extendedDateElement;
    
        @XmlAttribute(name = "dateAttribute")
        @XmlSchemaType(name = "date")
        protected XMLGregorianCalendar dateAttribute;
    
        @XmlAttribute(name = "dateTimeAttribute")
        @XmlSchemaType(name = "dateTime")
        protected XMLGregorianCalendar dateTimeAttribute;
    
        @XmlAttribute(name = "extendedDateAttribute")
        protected XMLGregorianCalendar extendedDateAttribute;
    
    }
    

    10-07 11:59