Xml JAXB/MOXy能否将元素值和元素属性映射到同一POJO字段?

Xml JAXB/MOXy能否将元素值和元素属性映射到同一POJO字段?,xml,xsd,jaxb,eclipselink,moxy,Xml,Xsd,Jaxb,Eclipselink,Moxy,我刚刚开始使用JAXB处理web服务,以将传入的SOAP文档解组到我们的域类中。我遇到了一个技术挑战,这是由丹麦政府机构使用的OIO XML格式决定的。除其他外,该格式声明不允许将xml模式属性nillable用于xml元素定义。因此,我必须为我的挑战找到另一种解决办法 描述 我们有一些数字和日期可以由webservice客户端发送以更新应用程序。这些数字和日期映射到等效类型的POJO字段。挑战在于如何通过构造和发送正确的XML来重置此类POJO字段的值 发送12:31:34T01-01-201

我刚刚开始使用JAXB处理web服务,以将传入的SOAP文档解组到我们的域类中。我遇到了一个技术挑战,这是由丹麦政府机构使用的OIO XML格式决定的。除其他外,该格式声明不允许将xml模式属性nillable用于xml元素定义。因此,我必须为我的挑战找到另一种解决办法

描述 我们有一些数字和日期可以由webservice客户端发送以更新应用程序。这些数字和日期映射到等效类型的POJO字段。挑战在于如何通过构造和发送正确的XML来重置此类POJO字段的值

发送12:31:34T01-01-2010。。。。。将POJO字段更新为指定值

但是,我无法通过发送重置该字段,因为datetime元素不允许使用该字段

我也不能发送,因为OIO XML标准不允许发送

因此,我认为发送是一个严峻的解决方案,因为OIO XML标准不应禁止它

这是如何带来挑战的 如果startTime元素包含delete=“true”属性,那么对应的POJO字段应该设置为null; 如果没有delete属性,则将有效元素值传输到POJO字段

@XMLElement注释只允许我映射startTime值,例如

class MyClass{
   @XMLElement
   private Date startTime;
}
如何强制delete属性也影响MyClass.startTime字段的值

致以最良好的祝愿,
Jesper

您可以为此使用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类中,我们将在Date实例和具有两个属性(Date和boolean)的类之间进行转换

演示

以下演示代码可用于演示此代码:

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

}
进一步扩展:

  • 您可以通过添加@XmlElement(name=“foo”)注释来控制startTime属性的元素名称
  • 您可以使用MOXy的@XmlPath(“foo/bar”)注释(如@XmlPath())进一步控制XML表示
有关更多信息,请参阅:


您可以为此使用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类中,我们将在Date实例和具有两个属性(Date和boolean)的类之间进行转换

演示

以下演示代码可用于演示此代码:

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

}
进一步扩展:

  • 您可以通过添加@XmlElement(name=“foo”)注释来控制startTime属性的元素名称
  • 您可以使用MOXy的@XmlPath(“foo/bar”)注释(如@XmlPath())进一步控制XML表示
有关更多信息,请参阅:


    • 非常感谢您的回答。它似乎适用于非常简单的场景,但对于稍微复杂一些的场景,我没有成功

      我试着在我自己的领域里模仿你的代码,只是我在整理数据而不是编组

      适配器类:

      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();    
      }
      
      带批注的工作域类:

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

      演示

      文件:

      <?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>
      
      但是,在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=011]
      
      我是做错了什么,还是莫西不支持这种情况


      另外,我尝试过使用MOXy 2.1.1和2.2.0-M3,结果相同

      非常感谢您的回答。它似乎适用于非常简单的场景,但对于稍微复杂一些的场景,我没有成功

      我试着在我自己的领域里模仿你的代码,只是我在整理数据而不是编组

      适配器类:

      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();    
      }
      
      带批注的工作域类:

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

      演示

      文件:

      <?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>
      
      但是,在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=011]
      
      我是做错了什么,还是莫西不支持这种情况


      另外,在回答您的第二个问题时,我尝试使用MOXy 2.1.1和2.2.0-M3,结果相同

      当我运行代码(使用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适配器中的逻辑以处理特定的XML示例。在XML片段中,您为映射到“forNavn”属性的“PersonGivenName”元素指定delete=“true”和一个值,在这种情况下,null或值是否应获胜

      <?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>
      
      更新:


      我已经通过电子邮件向您发送了我用来调试您的问题的Java项目,您介意运行它以查看是否得到相同的结果吗?希望您能得到相同的输出,我们可以找出导致错误行为的增量。

      回答您的第二个问题:

      当我运行代码(使用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适配器t中的逻辑