2010-10-19 112 views
1

我剛開始使用JAXB處理Web服務,將傳入的SOAP文檔解組到我們的域類中。我遇到了一個技術挑戰,這是由丹麥政府機構使用的OIO XML格式規定的。除其他外,格式規定不允許將xml模式屬性用於xml元素降級。因此我必須爲我的挑戰找到另一種解決方案。JAXB/MOXy可以將元素值和元素屬性映射到相同的POJO字段嗎?

描述 我們有一些數字和日期可以由web服務客戶端發送來更新應用程序。這些數字和日期被映射到等效類型的POJO字段。面臨的挑戰是如何通過構建和發送正確的XML來重置POJO字段的價值。

發送12:31:34T01-01-2010 .....將更新POJO字段到指定的值。

但是我可以不通過發送重置該字段,因爲它不允許用於日期時間元素。

我也不能發送,因爲它是OIO XML標準所不允許的。

因此,我作爲一個嚴峻的解決方案計劃發送,因爲它不應該被OIO XML標準拒絕。

如何引導一個挑戰,即如果startTime元素包含delete =「true」屬性,那麼 應該將相應的POJO字段設置爲null; 如果a沒有刪除屬性,則將有效元素值傳送到POJO字段。

@XMLElement註釋僅允許我繪製startTime值,例如,

class MyClass{ 
    @XMLElement 
    private Date startTime; 
} 

如何強制刪除屬性也影響MyClass.startTime字段的值?

最好的問候, 加斯帕

回答

1

你可以使用一個XmlAdapter這一點。該適配器將轉換爲/從一個日期對象。適應對象將具有兩個屬性。第一個使用@XmlValue註解的日期屬性值以及用@XmlAttribute註解的布爾值屬性。我明天會發佈一個完整的例子。

MyClass的

我們將與註釋的@XmlJavaTypeAdapter StartTime屬性。

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

@XmlRootElement 
class MyClass{ 

    private Date startTime; 

    @XmlJavaTypeAdapter(DateAdapter.class) 
    public Date getStartTime() { 
     return startTime; 
    } 

    public void setStartTime(Date startTime) { 
     this.startTime = startTime; 
    } 

} 

DateAdapter

在DateAdapter類,我們將日期的實例,並帶有兩個屬性(日期&布爾)類之間的轉換。

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

public class DateAdapter extends XmlAdapter<AdaptedDate, Date> { 

    @Override 
    public AdaptedDate marshal(Date date) throws Exception { 
     AdaptedDate adaptedDate = new AdaptedDate(); 
     adaptedDate.setDate(date); 
     return adaptedDate; 
    } 

    @Override 
    public Date unmarshal(AdaptedDate adaptedDate) throws Exception { 
     return adaptedDate.getDate(); 
    } 

} 

AdaptedDate

這是我們的最新信息,這兩個屬性表示。它由XmlAdapter構造。

import java.util.Date; 
import javax.xml.bind.annotation.XmlAttribute; 
import javax.xml.bind.annotation.XmlValue; 

public class AdaptedDate { 

    private Date date; 

    @XmlValue 
    public Date getDate() { 
     return date; 
    } 

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

    @XmlAttribute 
    public Boolean isDelete() { 
     if(null == date) { 
      return true; 
     } 
     return null; 
    } 

} 

演示

下面的演示代碼可以用來演示此代碼:

import java.util.Date; 
import javax.xml.bind.JAXBContext; 
import javax.xml.bind.Marshaller; 

public class Demo { 

    public static void main(String[] args) throws Exception { 
     JAXBContext jc = JAXBContext.newInstance(MyClass.class); 
     System.out.println(jc); 
     Marshaller marshaller = jc.createMarshaller(); 
     marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); 

     MyClass myClass1 = new MyClass(); 
     marshaller.marshal(myClass1, System.out); 

     MyClass myClass2 = new MyClass(); 
     myClass2.setStartTime(new Date()); 
     marshaller.marshal(myClass2, System.out); 
    } 

} 

進一步擴展:

  • 您可以控制的元素名稱startTime屬性通過添加@XmlElement(name =「foo」)annotati上。
  • 可以使用莫西的@XmlPath( 「富/酒吧」)註解像@XmlPath()進一步控制XML表示

欲瞭解更多信息,請參閱:

1

非常感謝您的回答。它似乎適用於一個非常簡單的場景,但對於稍微複雜的場景我沒有成功。

我試圖模仿你在我自己的領域的代碼,只有我urialrshalling數據而不是marshalling。

適配器類:

public class DeleteStringAdapter extends XmlAdapter<DeletableString, String> { 

@Override 
public DeletableString marshal(String value) throws Exception { 
    System.out.println("MARSHALL: " + value); 
    return new DeletableString(value); 
} 

@Override 
public String unmarshal(DeletableString v) throws Exception { 
    System.out.println("UNMARSHALL: " + v); 

    if(v.isDelete() != null && v.isDelete()){ 
     return null; 
    } 

    return v.getValue();  
} 

}

適配器類型:

public class DeletableString { 

public DeletableString() { 
} 

public DeletableString(String value) { 
    this.value = value; 
} 

private Boolean delete; 

private String value; 

@XmlAttribute 
public Boolean isDelete() { 
    return delete; 
} 

public void setDelete(Boolean delete) { 
    this.delete = delete; 
} 

@XmlValue 
public String getValue() { 
    return value; 
} 

public void setValue(String value) { 
    this.value = value; 
} 

@Override 
public String toString() { 
    return DeletableString.class.getSimpleName() + "[delete=" + isDelete() + ", value=" + value + "]"; 
} 
} 

工作域類與註釋:

@XmlAccessorType(XmlAccessType.PROPERTY) 
@XmlRootElement(name = "xml-fragment") 
public class SimpleNavne implements Serializable{ 

private String forNavn = ""; 

private String fornavneMrkKode = ""; 

@XmlElement(name="PersonGivenName") 
@XmlJavaTypeAdapter(value = DeleteStringAdapter.class) 
public String getForNavn() { 
    return forNavn; 
} 

public void setForNavn(String forNavn) { 
    this.forNavn = forNavn; 
} 

@Override 
public String toString() { 
    StringBuilder builder = new StringBuilder(); 
    builder.append(SimpleNavne.class.getSimpleName() + "["); 
    builder.append("forNavn="); 
    builder.append(forNavn); 
    builder.append("]"); 
    return builder.toString(); 
} 

}

演示

public class AppTest { 

@Test 
public void testApp() throws Exception { 

    System.setProperty("jaxb.debug", "true"); 

    try{ 
     JAXBContext jaxbContext = JAXBContext.newInstance(SimpleNavne.class); 
     Unmarshaller unmarshaller = jaxbContext.createUnmarshaller(); 
     Object ps = unmarshaller.unmarshal(new File("./personname-test4.xml")); 

     System.out.println(ps); 

    }catch(Exception e){ 
     e.printStackTrace(); 
    } 
} 
} 

文件:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<ns:xml-fragment xmlns:ns="http://cpr.csc.com/navne"> 
<ns:PersonGivenName delete="false">010</ns:PersonGivenName> 
</ns:xml-fragment> 

上面似乎很好地工作,如果從輸出得出結論

SimpleNavne[forNavn=010] 

引進與@XMLPath在註釋字段時,但是我得到的問題SimpleName域類。

修改域類

@XmlAccessorType(XmlAccessType.PROPERTY) 
@XmlRootElement(name = "xml-fragment") 
public class SimpleNavne implements Serializable{ 

private String forNavn = ""; 

private String fornavneMrkKode = ""; 

@XmlElement(name="PersonGivenName") 
@XmlJavaTypeAdapter(value = DeleteStringAdapter.class) 
public String getForNavn() { 
    return forNavn; 
} 

public void setForNavn(String forNavn) { 
    this.forNavn = forNavn; 
} 

@XmlPath("/PersonGivenNameMarkingStructure/MarkingCode/text()") 
public String getFornavneMrkKode() { 
    return fornavneMrkKode; 
} 

public void setFornavneMrkKode(String forNavnMrk) { 
    this.fornavneMrkKode = forNavnMrk; 
} 

@Override 
public String toString() { 
    StringBuilder builder = new StringBuilder(); 
    builder.append(SimpleNavne.class.getSimpleName() + "["); 
    builder.append(", forNavn="); 
    builder.append(forNavn); 
    builder.append(", fornavneMrkKode="); 
    builder.append(fornavneMrkKode); 
    builder.append("]"); 
    return builder.toString(); 
} 

} 

修改文件:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<ns:xml-fragment xmlns:ns="http://cpr.csc.com/navne"> 
<ns:PersonGivenName delete="true">010</ns:PersonGivenName> 
<ns:PersonGivenNameMarkingStructure> 
    <ns:MarkingCode>011</ns:MarkingCode> 
</ns:PersonGivenNameMarkingStructure> 
</ns:xml-fragment> 

輸出是:

SimpleNavne[forNavn=010, fornavneMrkKode=] 

但應該是:

SimpleNavne[forNavn=010, fornavneMrkKode=011] 

我在做一些完全錯誤的事情,或者是MOXy不支持這種情況?

PS。我一直在使用莫西2.1.1和2.2.0-M3具有相同的結果

1

試圖在回答你的第二個問題:

當我運行的代碼(使用的EclipseLink 2.1 1.1)我得到以下的輸出:

20-Oct-2010 2:50:46 PM javax.xml.bind.ContextFinder find 
FINE: Trying to locate forum80/jaxb.properties 
20-Oct-2010 2:50:46 PM javax.xml.bind.ContextFinder loadJAXBProperties 
FINE: loading props from file:/C:/Workspaces/EclipseLink-Trunk/SCRATCH/bin/forum80/jaxb.properties 
20-Oct-2010 2:50:46 PM javax.xml.bind.ContextFinder find 
FINE: found 
20-Oct-2010 2:50:46 PM javax.xml.bind.ContextFinder safeLoadClass 
FINE: Trying to load org.eclipse.persistence.jaxb.JAXBContextFactory 
20-Oct-2010 2:50:46 PM javax.xml.bind.ContextFinder newInstance 
FINE: loaded org.eclipse.persistence.jaxb.JAXBContextFactory from jar:file:/C:/Workspaces/EclipseLink-2.1/eclipselink.jar!/org/eclipse/persistence/jaxb/JAXBContextFactory.class 
UNMARSHALL: DeletableString[delete=true, value=010] 
SimpleNavne[, forNavn=null, fornavneMrkKode=011] 

這個結果是你所看到的不同:

SimpleNavne[forNavn=010, fornavneMrkKode=] 

我們可能需要調整XML適配器中的邏輯來處理您的特定XML示例。在你的XML片段中,你指定了delete =「true」和一個映射到「forNavn」屬性的「PersonGivenName」元素的值,在這種情況下應該爲null還是值win?

<?xml version="1.0" encoding="UTF-8" standalone="yes"?> 
<ns:xml-fragment xmlns:ns="http://cpr.csc.com/navne"> 
    <ns:PersonGivenName delete="true">010</ns:PersonGivenName> 
    <ns:PersonGivenNameMarkingStructure> 
     <ns:MarkingCode>011</ns:MarkingCode> 
    </ns:PersonGivenNameMarkingStructure> 
</ns:xml-fragment> 

如果使用JAXB RI,我可以使「fornavneMrkKode」屬性返回空字符串。您是否有可能沒有正確指定使用EclipseLink MOXy的jaxb.properties?

的jaxb.properties文件應該住在同一個包中的以下項的模型類:

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

UPDATE:

我已經通過電子郵件通知您,我使用的Java項目調試你的問題,你會介意運行它,看看你是否得到相同的結果?希望你會得到相同的輸出,我們可以找出導致錯誤行爲的增量。

1

非常感謝您花時間在此。

至於你的最後一個響應的響應:Can JAXB/MOXy map element value and element attribute to same POJO field?

我使用jaxb.properties與出廠配置文件。我的輸出看起來除了缺失值幾乎等同於你:

21-10-2010 09:47:08 javax.xml.bind.ContextFinder find 
FINE: Trying to locate com/csc/cpr/ws/domain/jaxb.properties 
21-10-2010 09:47:08 javax.xml.bind.ContextFinder loadJAXBProperties 
FINE: loading props from file:/C:/Ajour/ajourworkspace/jaxb-test-with-schema/target/classes/com/csc/cpr/ws/domain/jaxb.properties 
21-10-2010 09:47:08 javax.xml.bind.ContextFinder find 
FINE: found 
21-10-2010 09:47:08 javax.xml.bind.ContextFinder safeLoadClass 
FINE: Trying to load org.eclipse.persistence.jaxb.JAXBContextFactory 
21-10-2010 09:47:08 javax.xml.bind.ContextFinder newInstance 
FINE: loaded org.eclipse.persistence.jaxb.JAXBContextFactory from jar:file:/C:/Users/jpedersen22/.m2/repository/org/eclipse/persistence/eclipselink/2.1.1/eclipselink-2.1.1.jar!/org/eclipse/persistence/jaxb/JAXBContextFactory.class 
UNMARSHALL: DeletableString[delete=true, value=010] 
SimpleNavne[forNavn=null, fornavneMrkKode=] 

正如你所看到的,然後莫西正在使用的JAXB實現。當DeleteStringAdapter未被使用時,我還使用了成功的@XMLPath註釋。即如果刪除@XmlJavaTypeAdapter(值= DeleteStringAdapter.class)從getFornavn,然後我得到如下:

21-10-2010 10:00:02 javax.xml.bind.ContextFinder find 
FINE: Trying to locate com/csc/cpr/ws/domain/jaxb.properties 
21-10-2010 10:00:02 javax.xml.bind.ContextFinder loadJAXBProperties 
FINE: loading props from file:/C:/Ajour/ajourworkspace/jaxb-test-with-schema/target/classes/com/csc/cpr/ws/domain/jaxb.properties 
21-10-2010 10:00:02 javax.xml.bind.ContextFinder find 
FINE: found 
21-10-2010 10:00:02 javax.xml.bind.ContextFinder safeLoadClass 
FINE: Trying to load org.eclipse.persistence.jaxb.JAXBContextFactory 
21-10-2010 10:00:02 javax.xml.bind.ContextFinder newInstance 
FINE: loaded org.eclipse.persistence.jaxb.JAXBContextFactory from jar:file:/C:/Users/jpedersen22/.m2/repository/org/eclipse/persistence/eclipselink/2.1.1/eclipselink-2.1.1.jar!/org/eclipse/persistence/jaxb/JAXBContextFactory.class 
SimpleNavne[forNavn=010, fornavneMrkKode=011] 

關於XML 010,然後將其製成測試不同的場景時,讓我的生活更輕鬆(有或沒有屬性)。無論如何,那麼我的DeleteStringAdapter可以讓delete屬性獲得優先級,這正是我想要的。

+0

我已經通過電子郵件通知您,我使用調試您的問題Java項目,你會介意運行它,看看你得到同樣的結果?希望你會得到相同的輸出,我們可以找出導致錯誤行爲的增量。 – 2010-10-21 13:41:00

0

我用你的項目取得了巨大的成功!通過比較我們的項目,我也找到了自己的問題。我已將DeletableString類放在單獨的包中,但忘記添加package-info.java文件。添加以下內容的文件使整個事情工作:

@XmlSchema(namespace = "http://cpr.csc.com/navne", elementFormDefault = XmlNsForm.QUALIFIED) 
package com.csc.cpr.ws.domain.customtypes; 

import javax.xml.bind.annotation.XmlNsForm; 
import javax.xml.bind.annotation.XmlSchema; 

非常感謝您的幫助。它一直很有價值

最好的問候, 加斯帕

+0

沒問題,很高興我能幫上忙。 – 2010-10-21 15:13:41

相關問題