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

时间:2013-04-10 21:04:20

标签: java xml generics jaxb

我正在尝试创建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类型。我可以手动更改它,但我不必记住每次重新生成jaxb文件时都要更新它。有没有人知道是否有手动设置@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 个答案:

答案 0 :(得分:1)

更新

总结:

  1. 您有一个在某处使用日期样式的架构,而您无法更改架构
  2. 您有一些使用该架构的XML数据并使用时区指定一些日期(因此它是yyyy-MM-ddXXX格式)
  3. 您要从该文件中日期的表示中删除XXX部分(日期本身不发布任何时区,日期只是一个数字)
  4. 所以这可能是一个示例模式:

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

    END OF UPDATE


    抱歉,评论太长了......

    我无法理解:

    1. 你为什么要使用XmlGregorianCalendar
    2. 您为什么要marshal / unmarshalserialize / deserialize)加入相同的数据结构?
    3. 你为什么要删除时区??
      1. 我使用简单java.util.Date
      2. marshal / unmarshal应该始终涉及String s(至少对于XML)
      3. 我真的没有理由任意删除一段约会代表。也许你想以绝对的方式序列化它。
      4. 然而

        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 :(得分: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扩展名的不同文件中,并在需要的任何地方使用它。