我刚刚开始使用JAXB处理Web服务,以将传入的SOAP文档解组到我们的域类。我遇到了技术挑战,这是由丹麦政府机构中使用的OIO XML格式决定的。格式规定,不允许使用xml schema属性nillable进行xml元素declation。因此,我必须为我的挑战找到另一种解决方案。
描述 我们有一些数字和日期可以由Web服务客户端发送以更新应用程序。这些数字和日期映射到等效类型的POJO字段。挑战在于如何通过构造和发送正确的XML来重置这样的POJO字段的值。
发送12:31:34T01-01-2010 .....将POJO字段更新为指定值。
但是我可以通过发送来重置字段,因为不允许使用datetime元素。
我也无法发送,因为OIO XML标准不允许这样做。
因此,我作为一个严格的解决方案计划发送,因为它不应该被OIO XML标准禁止。
这是如何带来挑战的 如果startTime元素包含delete =“true”属性,则相应的POJO字段应设置为null; 如果a没有delete属性,则将有效元素值传递给POJO字段。
@XMLElement注释只允许我映射startTime值,例如
class MyClass{
@XMLElement
private Date startTime;
}
如何强制delete属性也影响MyClass.startTime字段的值?
祝你好运, 的Jesper
答案 0 :(得分: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类中,我们将在Date的实例和具有两个属性的类(date&amp; boolean)之间进行转换。
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);
}
}
进一步扩展:
有关详细信息,请参阅:
答案 1 :(得分:1)
非常感谢您的回答。它似乎适用于一个非常简单的场景,但对于稍微复杂的场景我没有成功。
我试图在我自己的域中模仿你的代码,只是我是ummarshalling数据而不是编组。
适配器类:
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]
但是在SimpleName域类中引入用@XMLPath注释的字段时会遇到问题。
修改后的域类
@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。我尝试使用MOXy 2.1.1和2.2.0-M3,结果相同
答案 2 :(得分:1)
回答你的第二个问题:
当我运行你的代码时(使用EclipseLink 2.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 Adapter中的逻辑来处理您的特定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”属性返回空字符串。您是否有可能没有正确指定jaxb.properties来使用EclipseLink MOXy?
jaxb.properties文件应与模型类位于同一个包中,并带有以下条目:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
<强>更新强>
我已经通过电子邮件向您发送了用于调试问题的Java项目,您是否介意运行它以查看是否得到相同的结果?希望你能获得相同的输出,我们可以找出导致错误行为的增量。
答案 3 :(得分: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=]
如您所见,MOXy是正在使用的JAXB实现。当DeleteStringAdapter未使用时,我也成功使用了@XMLPath注释。即如果我从getFornavn中删除@XmlJavaTypeAdapter(value = DeleteStringAdapter.class),那么我得到以下内容:
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属性采取presedence,这正是我想要的。
答案 4 :(得分: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;
非常感谢你的帮助。这是非常有价值的
祝你好运, 的Jesper