JAXB 2.x:如何从父类重写XmlElement注释 - Mission Impossible?

bas*_*ero 8 java inheritance jaxb marshalling jaxb2

为什么这不可能?它似乎很简单,但它的行为并不像预期的那样.

简介:类A使用聚合的DataA bean,而类B(类A的子类)使用聚合的DataB bean(而DataB扩展DataA).

我编写了这些测试类来可视化和解释我的问题:

A类:

package test;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name="root")
public class A {

  private DataA source = new DataA();

  @XmlElement(name="source")
  public DataA getSource() {
    return source;
  }

  public void setSource(DataA source) {
    this.source = source;
  }

}
Run Code Online (Sandbox Code Playgroud)

和它的DataA类(我使用了FIELD注释,以便所有字段都被编组):

package test;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;

@XmlAccessorType(XmlAccessType.FIELD)
public class DataA {

    public String string1 = "1";
    public String string2 = "2";

}
Run Code Online (Sandbox Code Playgroud)

现在是B类(A类的子类):我的目标是重用A的功能,并通过使用DataB bean重用DataA bean中的属性:

package test;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name="root")
public class B extends A {

  private DataB source = new DataB();

  public DataB getSource() {
    return this.source;
  }

  public void setSource(DataB source) {
    this.source = source;
  }

}
Run Code Online (Sandbox Code Playgroud)

它对应的DataB bean如下所示:

package test;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;

@XmlAccessorType(XmlAccessType.FIELD)
public class DataB extends DataA {
    public String string3 = "3";
}
Run Code Online (Sandbox Code Playgroud)

现在,当我编组A类的实例时,它给出了这个输出:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
  <source>
    <string1>1</string1>
    <string2>2</string2>
  </source>
</root>
Run Code Online (Sandbox Code Playgroud)

当我编组B类实例时,我得到了相同的结果:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
  <source>
    <string1>1</string1>
    <string2>2</string2>
  </source>
</root>
Run Code Online (Sandbox Code Playgroud)

但我预计string3也会被编组,但它只是编写bean DataA的属性!为什么?在考虑OOP时,这并不是非常直观.

当我在Class B上设置@XmlElement注释时......就像这样:

@XmlElement
public DataB getSource() {
    return this.source;
}
Run Code Online (Sandbox Code Playgroud)

...然后该属性被编组两次,因为它曾经由父类和子类注释.这也是我不想要的:

现在的输出是:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <source xsi:type="dataB" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
        <string1>1</string1>
        <string2>2</string2>
        <string3>3</string3>
    </source>
    <source>
        <string1>1</string1>
        <string2>2</string2>
        <string3>3</string3>
    </source>
</root>
Run Code Online (Sandbox Code Playgroud)

我对JAXB的期望是以下XML:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<root>
    <source>
        <string1>1</string1>
        <string2>2</string2>
        <string3>3</string3>
    </source>
</root>
Run Code Online (Sandbox Code Playgroud)

任何提示如何调整JAXB以产生预期的结果?感谢您的任何反馈.

bdo*_*han 3

只是不要在类 B 上注释源属性。源属性已映射到父类上,不应再次映射到子类上。由于您正在注释 get/set 方法,因此将在类 B 上调用适当的 get/set 方法。

package test;

import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;

@XmlAccessorType(XmlAccessType.NONE)
@XmlRootElement(name="root")
public class B extends A {

  private StringBuffer source = null;

  public String getSource() {
    return source.toString();
  }

  public void setSource(String source) {
    this.source = new StringBuffer(source);
  }
}
Run Code Online (Sandbox Code Playgroud)

更新

Metro JAXB(参考实现)中可能存在错误。当我使用EclipseLink JAXB (MOXy)运行这个更新的示例时,我得到以下输出:

<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
   <source>
      <string1>1</string1>
      <string2>2</string2>
   </source>
</root>
<?xml version="1.0" encoding="UTF-8"?>
<root xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="b">
   <source xsi:type="dataB">
      <string1>1</string1>
      <string2>2</string2>
      <string3>3</string3>
   </source>
</root>
Run Code Online (Sandbox Code Playgroud)

这可以用以下代码重现:

package test;

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(A.class, B.class);

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);

        A a = new A();
        DataA da = new DataA();
        da.string1 = "1";
        da.string2 = "2";
        a.setSource(da);
        marshaller.marshal(a, System.out);

        B b = new B();
        DataB db = new DataB();
        db.string1 = "1";
        db.string2 = "2";
        db.string3 = "3";
        b.setSource(db);
        marshaller.marshal(b, System.out);
    }
}
Run Code Online (Sandbox Code Playgroud)

要将 MOXy 用作 JAXB 实现,您需要在模型包(测试)中提供名为 jaxb.properties 的文件,其中包含以下条目:

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
Run Code Online (Sandbox Code Playgroud)