2010-08-25 40 views
4

我用XML結構看起來像這樣的工作:JAXB:映射獨立的日期和時間元素,一個屬性

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

而且我要的日期和時間映射到一個單一的DateCalendar財產我的豆子。這可能使用jaxb註釋? javax.xml.bind.annotation.adapters.XmlAdapter類似乎可能可以做到這一點,但我不得不承認我沒有完全理解它的javadoc。

一種解決方法是創建用於在一個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

除了使用JAXB RI(地鐵),你可以使用莫西JAXB實現(我的技術負責人)的。它有一些擴展可以使這種情況很容易映射。

jaxb.properties

要使用莫西爲您的JAXB實現,你需要添加一個名爲JAXB的文件。在同一個包的屬性與以下項的模型類:

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

我們可以leverate @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目前發佈的版本支持上,但有一個稍微不同的配置。如果您有興趣探索此選項,我們可以討論適當的設置。

+0

再次感謝廣泛的答覆,我會嘗試EclipseLink MOXY。 – 2010-08-26 13:40:55

+0

我的博客上提供了一些額外的詳細信息:http://bdoughan.blogspot.com/2010/08/xmltransformation-going-beyond.html – 2010-08-26 14:02:03

+0

找到了一些時間來嘗試一下,這在我的測試中正常工作。我對DateAttributeTransformer做了一些修改,用同樣不太好但更靈活的東西來代替'contains'檢查。我會將修改後的來源添加到問題中。 – 2010-08-31 23:03:34

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> 

更多信息參見:

+0

謝謝,XmlAdapter的一個很好的例子,比javadoc的HashMap例子更清晰。我的例子xml有點簡單,我實際上可以在該元素中包含其他元素和其他日期/時間組合。如果兩個日期/時間元素不包含在另一個元素中,是否可以適應它們?我會用更完整的例子更新這個問題。 – 2010-08-25 13:43:38

+0

我已經提交了第二個答案,描述瞭如何使用MOXY JAXB擴展來完成http://stackoverflow.com/questions/3565621/jaxb-mapping-separate-date-and-time-elements-to-one-property/ 3567927#3567927。 – 2010-08-25 16:06:38

相關問題