Подтвердить что ты не робот

JAXB Bindings File Sets @XmlElement для String вместо XMLGregorianCalendar

Я пытаюсь создать 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>
4b9b3361

Ответ 1

UPDATE

подведение итогов:

  • у вас есть схема, которая где-то использует стиль даты, и вы не можете изменить схему
  • у вас есть XML-данные, которые используют эту схему и указывают какую-либо дату с часовым поясом (так что это формат yyyy-MM-ddXXX)
  • вы хотите удалить часть 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/unmarshal (serialize/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
    }
}

Ответ 2

Вы должны указать тип 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, и я использую его везде, где он мне нужен.