首页 文章

JAXB绑定文件将@XmlElement类型设置为String而不是XMLGregorianCalendar

提问于
浏览
19

我正在尝试创建 XmlAdapter ,它接收 XMLGregorianCalendar 并输出 XMLGregorianCalendar . 目的是简单地在解组数据时从元素中删除时区数据 .

它看起来像这样:

public class TimezoneRemoverAdapter extends XmlAdapter<XMLGregorianCalendar, XMLGregorianCalendar> {
    public XMLGregorianCalendar unmarshal(XMLGregorianCalendar xgc) {
        if(xgc == null) {
            return null;
        }
        xgc.setTimezone(DatatypeConstants.FIELD_UNDEFINED);
        return xgc;
    }

    public XMLGregorianCalendar marshal(XMLGregorianCalendar xgc) {
        return xgc;
    }
}

这适用于以下代码:

public class FooElement {
    @XmlElement(name="bar-date")
    @XmlJavaTypeAdapter(TimezoneRemoverAdapter.class)
    @XmlSchemaType(name = "date")
    protected XMLGregorianCalendar barDate;
}

不幸的是,当我使用 jaxb-bindings.xml 文件生成代码时,上面的代码如下所示:

public class FooElement {
    @XmlElement(name="bar-date", type=java.lang.String.class)
    @XmlJavaTypeAdapter(TimezoneRemoverAdapter.class)
    @XmlSchemaType(name = "date")
    protected XMLGregorianCalendar barDate;
}

它将类型设置为 String ,因此我的上述方法不起作用 . 类型 String 设置将覆盖它应该的 XMLGregorianCalendar 类型 . 我可以手动更改它,但是'd rather not have to remember to update it every time the jaxb files are regenerated. Does anyone know if there'是一个手动设置 @XmlElement 类型或忽略它的选项?

以下是 jaxb-bindings.xml 文件的相关部分:

<jxb:bindings node=".//xs:element[@name=bar-date]">
    <jxb:property>
        <jxb:baseType>
            <jxb:javaType name="javax.xml.datatype.XMLGregorianCalendar" adapter="foo.bar.TimezoneRemoverAdapter" />
        </jxb:baseType>
    </jxb:property>
</jxb:bindings>

2 回答

  • 1

    更新

    总结:

    • 您有一个在某处使用日期样式的模式,您无法更改模式

    • 你有一些使用该模式的XML数据,并用时区指定一些日期(所以它是 yyyy-MM-ddXXX 格式)

    • 您要从该文件中日期的 representation 删除 XXX 部分(日期本身不发送任何时区,日期只是一个数字)

    所以这可能是一个示例模式:

    <?xml version="1.0" encoding="UTF-8"?>
    <schema xmlns="http://www.w3.org/2001/XMLSchema" elementFormDefault="qualified">
        <element name="foo">
            <complexType>
                <sequence>
                    <element name="bar" type="date" minOccurs="1" maxOccurs="1"/>
                </sequence>
            </complexType>
        </element>
    </schema>
    

    这可能是一个示例数据:

    <?xml version="1.0" encoding="UTF-8"?>
    <foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="foo.xsd">
        <bar>2014-01-01+06:00</bar>
    </foo>
    

    这是JAXB注释类

    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public class Foo implements Serializable
    {
        private static final long serialVersionUID = 1L;
    
        @XmlElement(name = "bar")
        @XmlJavaTypeAdapter(DateAdapter.class)
        @XmlSchemaType(name = "date")
        private Date bar;
    
        // getters/setters
    }
    

    这是日期适配器

    public class DateAdapter extends XmlAdapter<String, Date>
    {
        @Override
        public String marshal(Date date)
        {
            DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
            df.setTimeZone(TimeZone.getTimeZone("GMT"));
            return df.format(date);
        }
    
        @Override
        public Date unmarshal(String date) throws ParseException
        {
            DateFormat df = new SimpleDateFormat("yyyy-MM-ddXXX");
            return df.parse(date);
        }
    }
    

    这是主要的,验证模式:

    public static void main(String[] args) throws JAXBException, SAXException
    {
        JAXBContext context = JAXBContext.newInstance(Foo.class);
    
        SchemaFactory sf = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
        Schema schema = sf.newSchema(Foo.class.getResource("/foo.xsd"));
    
        Unmarshaller unmarshaller = context.createUnmarshaller();
        unmarshaller.setSchema(schema);
        Foo foo = (Foo) unmarshaller.unmarshal(Foo.class.getResource("/foo.xml"));
        System.out.println("unmarshalled: " + foo.getBar());
    
        Marshaller marshaller = context.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.setProperty(Marshaller.JAXB_NO_NAMESPACE_SCHEMA_LOCATION, "foo.xsd");
        marshaller.setSchema(schema);
        marshaller.marshal(foo, System.out);
    }
    

    这是输出,时区已被删除,日期表示已明显改变

    unmarshalled: Tue Dec 31 19:00:00 CET 2013
    <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
    <foo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="foo.xsd">
        <bar>2013-12-31</bar>
    </foo>
    

    也许这个日期表示更改不是您所期望的,但这不是JAXB关注的问题,所代表的日期没有改变 .

    我忘了绑定反向生成Foo:

    <jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc" jaxb:extensionBindingPrefixes="xjc" jaxb:version="2.0">
        <jaxb:globalBindings>
            <xjc:javaType name="java.util.Date" xmlType="xsd:date" adapter="aaa.DateAdapter" />
        </jaxb:globalBindings>
    </jaxb:bindings>
    

    更新结束


    对不起,评论太久了......

    我无法理解:

    • 你为什么要用 XmlGregorianCalendar

    • 为什么你要 marshal / unmarshalserialize / deserialize )才能使用相同的数据结构?

    • 为什么要删除时区?

    • 我用直而简单的 java.util.Date

    • marshal / unmarshal 应始终涉及 String (至少对于XML)

    • 我真的没有理由任意删除一个日期表示 . 也许你想以绝对的方式序列化它 .

    然而

    public class DateAdapter extends XmlAdapter<String, Date>
    {
        @Override
        public String marshal(Date date)
        {
            DateFormat df = DateFormat.getDateTimeInstance();
            df.setTimeZone(TimeZone.getTimeZone("GMT"));
    
            return df.format(date);
        }
    
        @Override
        public Date unmarshal(String date) throws ParseException
        {
            DateFormat df = DateFormat.getDateTimeInstance();
            df.setTimeZone(TimeZone.getTimeZone("GMT"));
    
            return df.parse(date);
        }
    
        public static void main(String[] args) throws ParseException
        {
            DateAdapter adapter = new DateAdapter();
    
            String str = adapter.marshal(new Date());
            System.out.println(str); // 16-dic-2013 10.02.09  --> to gmt
    
            Date date = adapter.unmarshal(str);
            System.out.println(date); // Mon Dec 16 11:02:09 CET 2013  --> correct, i'm gmt+1
        }
    }
    
  • 1

    在您的情况下,您必须以不同的方式指定java类型:

    <jxb:javaType 
         name="javax.xml.datatype.XMLGregorianCalendar" 
         xmlType="xs:date"  
         printMethod="foo.bar.TimezoneRemoverAdapter.marshall" 
         parseMethod="foo.bar.TimezoneRemoverAdapter.unmarshall" 
    />
    

    它适用于我,我做了类似的更多适配器:

    <jaxb:globalBindings>    
         <xjc:serializable uid="12343" />
         <jaxb:javaType name="java.util.Date" xmlType="xs:date" printMethod="com.foo.DateAdapter.printDate" parseMethod="com.foo.DateAdapter.parseDate" />
         <jaxb:javaType name="java.util.Date" xmlType="xs:dateTime" printMethod="com.foo.DateAdapter.printDate" parseMethod="com.foo.DateAdapter.parseDate" />
         <jaxb:javaType name="java.util.Date" xmlType="xs:time" printMethod="com.foo.TimeAdapter.printTime" parseMethod="com.foo.TimeAdapter.parseTime" />
    </jaxb:globalBindings>
    

    我把上面的绑定作为 globalBindings 放在一个扩展名为 .xjb 的不同文件中,并且我在需要的地方使用它 .

相关问题