JAXB:将单独的日期和时间元素映射到一个属性

时间:2010-08-25 12:09:10

标签: java calendar jaxb

我正在使用如下所示的XML结构:

<ROOT>
    <ELEM_A>
        <A_DATE>20100825</A_DATE>
        <A_TIME>141500</A_TIME>
        <!-- other elements, maybe also other or date/time combinations -->
        <STRING>ABC</STRING>
    <ELEM_A>
    <ELEM_B>
        <B_DATE>20100825</B_DATE>
        <B_TIME>153000</B_TIME>
        <NUM>123</NUM>
        <C_DATE>20100825</C_DATE>
        <C_TIME>154500</C_TIME>
    </ELEM_B>
</ROOT>

我想将日期和时间映射到我的bean中的单个DateCalendar属性。这可能是使用jaxb注释吗?类javax.xml.bind.annotation.adapters.XmlAdapter似乎可以做到这一点,但我必须承认我并不完全理解它的javadoc。

解决方法是为日期和时间字符串创建其他setter,以便在Calendar属性中设置相应的值,如下所示:

private Calendar calendar;

public void setDate(String date) {
    if (calendar == null) {
        calendar = new GregorianCalendar();
    }
    calendar.set(YEAR, Integer.parseIn(date.substring(0, 4)));
    calendar.set(MONTH, Integer.parseIn(date.substring(4, 6))-1);
    calendar.set(DAY_OF_MONTH, Integer.parseIn(date.substring(6, 8)));
}

// Similar code for setTime

问题(除了附加代码)是我不能总是保证在时间值之前设置日期,但我想不出一个具体的例子,这可能会给出结果。

我们赞赏基于注释的解决方案的任何示例或上述代码的改进/反例。

编辑:我选择了Blaise Doughan给出的第二个答案,但修改了他的DateAttributeTransformer更灵活,并且不希望字段名称包含字符串“DATE”。字段名称取自字段上的XmlWriterTransformer注释:

@Override
public Object buildAttributeValue(Record record, Object instance, Session session) {
    try {
        String dateString = null;
        String timeString = null;

        String dateFieldName = null;
        String timeFieldName = null;
        // TODO: Proper Exception handling
        try {
            XmlWriteTransformers wts = instance.getClass().getDeclaredField(mapping.getAttributeName()).getAnnotation(XmlWriteTransformers.class);
            for (XmlWriteTransformer wt : wts.value()) {
                String fieldName = wt.xpath();
                if (wt.transformerClass() == DateFieldTransformer.class) {
                    dateFieldName = fieldName;
                } else {
                    timeFieldName = fieldName;
                }
            }
        } catch (NoSuchFieldException ex) {
            throw new RuntimeException(ex);
        } catch (SecurityException ex) {
            throw new RuntimeException(ex);
        }

        for(DatabaseField field : mapping.getFields()) {
            XMLField xfield = (XMLField)field;
            if(xfield.getXPath().equals(dateFieldName)) {
                dateString = (String) record.get(field);
            } else {
                timeString = (String) record.get(field);
            }
        }
        return yyyyMMddHHmmss.parseObject(dateString + timeString);
    } catch(ParseException e) {
        throw new RuntimeException(e);
    }
}

2 个答案:

答案 0 :(得分:5)

XmlAdapter是正确的方法:

具有日期属性的类

import java.util.Date;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;

@XmlRootElement(name="ROOT")
public class Root {

    private Date date;

    @XmlElement(name="ELEM")
    @XmlJavaTypeAdapter(DateAdapter.class)
    public Date getDate() {
        return date;
    }

    public void setDate(Date date) {
        this.date = date;
    }

}

XmlAdapter的实施

import java.text.SimpleDateFormat;
import java.util.Date;
import javax.xml.bind.annotation.adapters.XmlAdapter;

public class DateAdapter extends XmlAdapter<AdaptedDate, Date> {

    private SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");
    private SimpleDateFormat HHmmss = new SimpleDateFormat("HHmmss");
    private SimpleDateFormat yyyyMMddHHmmss = new SimpleDateFormat("yyyyMMddHHmmss");

    @Override
    public Date unmarshal(AdaptedDate v) throws Exception {
        String dateString = v.getDate() + v.getTime();
        return yyyyMMddHHmmss.parse(dateString);
    }

    @Override
    public AdaptedDate marshal(Date v) throws Exception {
        AdaptedDate adaptedDate = new AdaptedDate();
        adaptedDate.setDate(yyyyMMdd.format(v));
        adaptedDate.setTime(HHmmss.format(v));
        return adaptedDate;
    }

}

改编的日期对象

import javax.xml.bind.annotation.XmlElement;

public class AdaptedDate {

    private String date;
    private String time;

    @XmlElement(name="DATE")
    public String getDate() {
        return date;
    }

    public void setDate(String date) {
        this.date = date;
    }

    @XmlElement(name="TIME")
    public String getTime() {
        return time;
    }

    public void setTime(String time) {
        this.time = time;
    }

}

示例程序

import java.io.File;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);

        File xml = new File("input.xml");
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        Root root = (Root) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(root, System.out);
    }
}

XML文档

<?xml version="1.0" encoding="UTF-8"?>
<ROOT>
    <ELEM>
        <DATE>20100825</DATE>
        <TIME>141500</TIME>
    </ELEM>
</ROOT> 

有关详细信息,请参阅:

答案 1 :(得分:2)

您可以使用MOXy JAXB实现(我是技术主管),而不是使用JAXB RI(Metro)。它有一些扩展,可以很容易地映射这个场景。

<强> jaxb.properties

要将MOXy用作JAXB实现,您需要在与模型类相同的包中添加名为jaxb.properties的文件,并使用以下条目:

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory

<强>根

import javax.xml.bind.annotation.*;

@XmlRootElement(name="ROOT")
@XmlAccessorType(XmlAccessType.FIELD)
public class Root {

    @XmlElement(name="ELEM_A")
    private ElemA elemA;

    @XmlElement(name="ELEM_B")
    private ElemB elemB;

}

<强> ElemA

我们可以提升@XmlTransformation。它在概念上与XmlAdapter类似,但更容易在映射之间共享。

import java.util.Date;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;

import org.eclipse.persistence.oxm.annotations.XmlReadTransformer;
import org.eclipse.persistence.oxm.annotations.XmlTransformation;
import org.eclipse.persistence.oxm.annotations.XmlWriteTransformer;
import org.eclipse.persistence.oxm.annotations.XmlWriteTransformers;

@XmlAccessorType(XmlAccessType.FIELD)
public class ElemA {

    @XmlTransformation
    @XmlReadTransformer(transformerClass=DateAttributeTransformer.class)
    @XmlWriteTransformers({
        @XmlWriteTransformer(xpath="A_DATE/text()", transformerClass=DateFieldTransformer.class),
        @XmlWriteTransformer(xpath="A_TIME/text()", transformerClass=TimeFieldTransformer.class),
    })
    public Date aDate;

    @XmlElement(name="STRING")
    private String string;
}

<强> ElemB

import java.util.Date;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;

import org.eclipse.persistence.oxm.annotations.XmlReadTransformer;
import org.eclipse.persistence.oxm.annotations.XmlTransformation;
import org.eclipse.persistence.oxm.annotations.XmlWriteTransformer;
import org.eclipse.persistence.oxm.annotations.XmlWriteTransformers;

@XmlAccessorType(XmlAccessType.FIELD)
public class ElemB {

    @XmlTransformation
    @XmlReadTransformer(transformerClass=DateAttributeTransformer.class)
    @XmlWriteTransformers({
        @XmlWriteTransformer(xpath="B_DATE/text()", transformerClass=DateFieldTransformer.class),
        @XmlWriteTransformer(xpath="B_TIME/text()", transformerClass=TimeFieldTransformer.class),
    })
    private Date bDate;

    @XmlElement(name="NUM")
    private int num;

    @XmlTransformation
    @XmlReadTransformer(transformerClass=DateAttributeTransformer.class)
    @XmlWriteTransformers({
        @XmlWriteTransformer(xpath="C_DATE/text()", transformerClass=DateFieldTransformer.class),
        @XmlWriteTransformer(xpath="C_TIME/text()", transformerClass=TimeFieldTransformer.class),
    })
    private Date cDate;

}

<强> DateAttributeTransformer

属性转换器负责解组Date对象。

import java.text.ParseException;
import java.text.SimpleDateFormat;

import org.eclipse.persistence.internal.helper.DatabaseField;
import org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping;
import org.eclipse.persistence.mappings.transformers.AttributeTransformer;
import org.eclipse.persistence.sessions.Record;
import org.eclipse.persistence.sessions.Session;

public class DateAttributeTransformer implements AttributeTransformer {

    private AbstractTransformationMapping mapping;
    private SimpleDateFormat yyyyMMddHHmmss = new SimpleDateFormat("yyyyMMddHHmmss");

    public void initialize(AbstractTransformationMapping mapping) {
        this.mapping = mapping;
    }

    public Object buildAttributeValue(Record record, Object instance, Session session) {
        try {
            String dateString = null;
            String timeString = null;

            for(DatabaseField field : mapping.getFields()) {
                if(field.getName().contains("DATE")) {
                    dateString = (String) record.get(field);
                } else {
                    timeString = (String) record.get(field);
                }
            }
            return yyyyMMddHHmmss.parseObject(dateString + timeString);
        } catch(ParseException e) {
            throw new RuntimeException(e);
        }
    }

}

<强> DateFieldTransformer

字段转换器负责编组Date对象。

import java.text.SimpleDateFormat;
import java.util.Date;

import org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping;
import org.eclipse.persistence.mappings.transformers.FieldTransformer;
import org.eclipse.persistence.sessions.Session;

public class DateFieldTransformer implements FieldTransformer {

    private AbstractTransformationMapping mapping;
    private SimpleDateFormat yyyyMMdd = new SimpleDateFormat("yyyyMMdd");

    public void initialize(AbstractTransformationMapping mapping) {
        this.mapping = mapping;
    }

    public Object buildFieldValue(Object instance, String xPath, Session session) {
        Date date = (Date) mapping.getAttributeValueFromObject(instance);
        return yyyyMMdd.format(date);
    }

}

<强> TimeFieldTransformer

import java.text.SimpleDateFormat;
import java.util.Date;

import org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping;
import org.eclipse.persistence.mappings.transformers.FieldTransformer;
import org.eclipse.persistence.sessions.Session;

public class TimeFieldTransformer implements FieldTransformer {

    private AbstractTransformationMapping mapping;
    private SimpleDateFormat HHmmss = new SimpleDateFormat("HHmmss");

    public void initialize(AbstractTransformationMapping mapping) {
        this.mapping = mapping;
    }

    public Object buildFieldValue(Object instance, String xPath, Session session) {
        Date date = (Date) mapping.getAttributeValueFromObject(instance);
        return HHmmss.format(date);
    }

}

示例程序

import java.io.File;

import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;

public class Demo {

    public static void main(String[] args) throws Exception {
        JAXBContext jc = JAXBContext.newInstance(Root.class);
        System.out.println(jc);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        File xml = new File("src/forum41/input.xml");
        Root root = (Root) unmarshaller.unmarshal(xml);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(root, System.out);
    }

}

XML文档

<ROOT> 
    <ELEM_A> 
        <A_DATE>20100825</A_DATE> 
        <A_TIME>141500</A_TIME>
        <!-- other elements, maybe also other or date/time combinations --> 
        <STRING>ABC</STRING> 
    </ELEM_A> 
    <ELEM_B> 
        <B_DATE>20100825</B_DATE> 
        <B_TIME>153000</B_TIME>
        <NUM>123</NUM> 
        <C_DATE>20100825</C_DATE> 
        <C_TIME>154500</C_TIME>
    </ELEM_B> 
</ROOT> 

如上所示的代码要求EclipseLink 2.2目前正在开发中。这里有夜间建筑:

当前发布的EclipseLink 2.1版本支持上述版本,但配置略有不同。如果您有兴趣探索此选项,我们可以讨论适当的设置。