代码之家  ›  专栏  ›  技术社区  ›  Jörn Horstmann

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

  •  4
  • Jörn Horstmann  · 技术社区  · 15 年前

    我使用的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>
    

    我想把日期和时间映射到一个 Date Calendar javax.xml.bind.annotation.adapters.XmlAdapter 看起来它可能可以做到这一点,但我不得不承认我不完全理解它的javadoc。

    一种解决方法是为日期和时间字符串创建额外的setter,这些字符串在 日历 这样的属性:

    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 回复  |  直到 9 年前
        1
  •  2
  •   bdoughan    15 年前

    您可以使用MOXy JAXB实现(我是技术负责人),而不是使用jaxbri(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;
    
    }
    

    埃莱玛

    我们可以利用@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;
    }
    

    埃伦布

    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;
    
    }
    

    日期属性转换器

    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公司

    场转换器负责编组日期对象。

    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);
        }
    
    }
    

    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.1版本支持上述功能,但配置略有不同。如果您有兴趣探索此选项,我们可以讨论适当的设置。

        2
  •  5
  •   bdoughan    15 年前

    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 version="1.0" encoding="UTF-8"?>
    <ROOT>
        <ELEM>
            <DATE>20100825</DATE>
            <TIME>141500</TIME>
        </ELEM>
    </ROOT> 
    

    有关更多信息,请参阅: