JAXB和继承在封送时起作用;但不是解组



我想封送/取消封送从另一个类继承的类的对象。

我从Thing:类开始

import java.util.List;
public class Thing {
private List<String> strings;
public List<String> getStrings() {
return strings;
}
public void setStrings(List<String> strings) {
this.strings = strings;
}
}

我扩展了这个类,并用JAXB注释对它进行了注释。

import java.util.List;
import javax.xml.bind.annotation.*;
@XmlRootElement
public class JaxbThing extends Thing {
// @XmlElementWrapper(name = "list")
@XmlElementWrapper(name = "strings")
@XmlElement(name = "string")
public List<String> getStrings() {
return super.getStrings();
}
public void setStrings(List<String> string) {
super.setStrings(string);
}
}

然后我运行以下编组/解编组程序:

import java.io.File;
import java.util.Arrays;
import javax.xml.bind.*;
public class Main {
public static void main(String[] args) {
JaxbThing t = new JaxbThing();
t.setStrings(Arrays.asList("a", "b", "c"));
try {
File f = new File("jaxb-file.xml");
JAXBContext context = JAXBContext.newInstance(JaxbThing.class);
Marshaller m = context.createMarshaller();
m.marshal(t, f);
Unmarshaller um = context.createUnmarshaller();
JaxbThing t2 = (JaxbThing) um.unmarshal(f);
System.out.println(t2.getStrings());  // I expect to see [a, b, c]
} catch (JAXBException e) {
e.printStackTrace();
}
}
}

XML文件的内容是:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<jaxbThing>
<strings>
<string>a</string>
<string>b</string>
<string>c</string>
</strings>
</jaxbThing>

一切似乎都是正确的。但解组的结果让我感到惊讶,因为控制台显示:

[
]

当我期望看到[a, b, c]

如果我以这种方式注释strings属性:

@XmlElementWrapper(name = "list")
// @XmlElementWrapper(name = "strings")
@XmlElement(name = "string")
public List<String> getStrings() {
return super.getStrings();
}

然后控制台显示预期的[a, b, c]

我猜想JAXB解组器使用类Thing而不是JaxbThing来解组XML文件内容。事实上,如果我用@XmlTransient注释类Thing,我会得到预期的结果。

然而,我不理解JAXB的这种行为。

有人能给我解释一下吗?提前谢谢。

问题在于您正在重写父类的属性。JAXB认为JAXBThing具有">

  1. 一个名为strings(继承自String)的属性映射到元素strings
  2. 称为strings的属性(在JAXBThing上定义)映射到元素strings下的元素string

如果在JAXBThing上的setStrings方法中放置一个断点,您将看到它第一次使用正确的数据调用,然后第二次使用覆盖初始集的不正确数据调用(因为它认为它们是共享setter的不同属性)。

您总是可以通过用@XmlTransientThing类进行注释来删除它。

import java.util.List;
import javax.xml.bind.annotation.XmlTransient;
@XmlTransient
public class Thing {
private List<String> strings;
public List<String> getStrings() {
return strings;
}
public void setStrings(List<String> strings) {
System.out.println("Thing" + strings);
this.strings = strings;
}
}

有关更多信息

  • http://blog.bdoughan.com/2011/06/ignoring-inheritance-with-xmltransient.html

非常感谢您的回答。我已经按照你的建议做了。你是对的,setStrings方法被调用了两次。但是,至少在我的实验中,它从来没有被正确的值调用。

Bellow I提供修改后的样本代码。我添加了几行来跟踪执行情况。我还激活了整理器格式的输出,以便更好地了解解组过程中会发生什么。

基类Thing

import java.util.List;
public class Thing {
private List<String> strings;
public List<String> getStrings() {
return strings;
}
public void setStrings(List<String> strings) {
/*new*/ System.out.printf("Thing.setStrings called. Prev. this.strings: %s (size %s). Received strings param: %s", this.strings, (this.strings!=null ? this.strings.size():"null"),strings);
this.strings = strings;
/*new*/ System.out.printf(". New this.strings: %s (size %s).%n%n", this.strings, (this.strings!=null ? this.strings.size():"null"));
}
}

扩展类JaxbThing

import java.util.List;
import javax.xml.bind.annotation.*;
@XmlRootElement
public class JaxbThing extends Thing {
// @XmlElementWrapper(name = "list")
@XmlElementWrapper(name = "strings")
@XmlElement(name = "string")
public List<String> getStrings() {
return super.getStrings();
}
public void setStrings(List<String> string) {
/*new*/ System.out.printf("JaxbThing.setStrings called. Received strings param: %s (size %s)%n",string,(string !=null?string.size():"null"));
super.setStrings(string);
}
}

测试程序:

import java.io.File;
import java.util.Arrays;
import javax.xml.bind.*;
public class Main {
public static void main(String[] args) {    
System.out.println("---- Initiallizing");
JaxbThing t = new JaxbThing();
t.setStrings(Arrays.asList("a", "b", "c"));
try {
File f = new File("jaxb-file.xml");
JAXBContext context = JAXBContext.newInstance(JaxbThing.class);
System.out.println("---- Marshalling");
Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);   /*new*/
m.marshal(t, f);
System.out.println("---- UnMarshalling");
Unmarshaller um = context.createUnmarshaller();
JaxbThing t2 = (JaxbThing) um.unmarshal(f);
System.out.println("---- Showing results");
System.out.println(t2.getStrings());  // I expect to see [a, b, c]
} catch (JAXBException e) {
e.printStackTrace();
}
}
}

结果如下(我已经对行进行了编号):

1  ---- Initiallizing
2  JaxbThing.setStrings called. Received strings param: [a, b, c] (size 3)
3  Thing.setStrings called. Prev. this.strings: null (size null). Received strings param: [a, b, c]. New this.strings: [a, b, c] (size 3).
4  
5  ---- Marshalling
6  ---- UnMarshalling
7  JaxbThing.setStrings called. Received strings param: [] (size 0)
8  Thing.setStrings called. Prev. this.strings: null (size null). Received strings param: []. New this.strings: [] (size 0).
9  
10 JaxbThing.setStrings called. Received strings param: [
] (size 1)
11 Thing.setStrings called. Prev. this.strings: [
] (size 1). Received strings param: [
]. New this.strings: [
] (size 1).
12 
13 ---- Showing results
14 [
]

在解组过程中,将构建一个新的JaxbThing,首先用0长度的字符串列表调用它的JaxbThing.setStrings(第7行)。因此,Thing.setStrings被调用(第8行)。对象的字符串字段(this.strings)的上一个值为null。然后,再次调用JaxbThing.setStrings(第10行),但现在有一个字符串列表,其中包含一个带有"\n"字符的字符串加上4个空格(我想这对应于XML文件的缩进)。两点:

  1. JaxbThing.setStrings从来没有用正确的值调用过(除非我没有见过)
  2. JaxbThing.setStrings的第二次调用(线路10)引起对Thing.setStrings的第二调用(线路11)。但现在this.strings的前一个值不是在第8行对Thing.setStrings的前一次调用中设置的字符串的0长度列表。它已经是作为参数传递的"\n"字符加上4个空格

有任何线索可以解释这种行为吗?

最新更新