当然,如果输入中没有任何元素触发该操作,则永远不会调用适配器。在您链接的那个示例中发生的情况是出现了一个具有空值的属性:
<element att="" />
这里的关键是有一个att 属性,但它有一个空字符串。因此,JAXB 解组器会将其呈现给二传手。但是,由于上面声明了一个适配器,它会通过那里并变成一个空值。
如果你有这个
<element />
这是另一个故事。没有att 属性,因此永远不需要调用setter。
出现但没有内容的元素与完全没有元素之间是有区别的。前者基本上可以认为是包含一个空的String,而后者只是“不存在”。
编辑:用这些类测试...
Bean.java
package jaxbadapter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name="Test")
public class Bean {
@XmlElement
@XmlJavaTypeAdapter(NullStringAdapter.class)
private String someValue;
public Bean() {
}
public String getSomeValue() {
return someValue;
}
public void setSomeValue(final String someValue) {
this.someValue = someValue;
}
}
NullStringAdapter.java
package jaxbadapter;
import javax.xml.bind.annotation.adapters.XmlAdapter;
public class NullStringAdapter extends XmlAdapter<String, String> {
@Override
public String unmarshal(final String v) throws Exception {
if("".equals(v)) {
return null;
}
return v;
}
@Override
public String marshal(final String v) throws Exception {
if(null == v) {
return "";
}
return v;
}
}
ObjectFactory.java
package jaxbadapter;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
@XmlRegistry
public class ObjectFactory {
public ObjectFactory() {
}
public Bean createBean() {
return new Bean();
}
@XmlElementDecl(name = "Test")
public JAXBElement<Bean> createTest(Bean value) {
return new JAXBElement<>(new QName("Test"), Bean.class, null, value);
}
}
Main.java
package jaxbadapter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.Marshaller;
import javax.xml.transform.stream.StreamResult;
public class Main {
public static void main(String[] args) throws Exception {
final JAXBContext context = JAXBContext.newInstance("jaxbadapter");
final Marshaller m = context.createMarshaller();
m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
final ObjectFactory of = new ObjectFactory();
final Bean b1 = new Bean();
final Bean b2 = new Bean();
b2.setSomeValue(null);
final Bean b3 = new Bean();
b3.setSomeValue("");
m.marshal(of.createTest(b1), System.out);
System.out.println("");
m.marshal(of.createTest(b2), System.out);
System.out.println("");
m.marshal(of.createTest(b3), System.out);
System.out.println("");
}
}
这是输出:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Test/>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Test/>
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Test>
<someValue></someValue>
</Test>
其实让我有点吃惊。然后我尝试将 getter 更改为 return someValue == null ? "" : someValue; 无济于事。然后在 getter 上设置一个断点,发现它永远不会被调用。
显然,JAXB 使用反射来尝试检索值,而不是在使用 XmlAccessType.FIELD 时通过 setter。铁杆。现在,您可以改用XmlAccessType.PROPERTY 并注释getter 或setter 来绕过它...
package jaxbadapter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlType(name="Test")
public class Bean {
private String someValue;
public Bean() {
}
@XmlElement
@XmlJavaTypeAdapter(NullStringAdapter.class)
public String getSomeValue() {
return someValue;
}
public void setSomeValue(final String someValue) {
this.someValue = someValue;
}
}
...但这仍然没有帮助。适配器的marshal 方法仅在最后一个设置了空字符串的测试用例中调用了一次。显然它首先调用了 getter,当它返回 null 时,它完全跳过了适配器的东西。
我能想出的唯一解决方案就是在此完全放弃使用适配器,并将替换项放在 getter 中,确保使用 XmlAccessType.PROPERTY:
package jaxbadapter;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlType;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
@XmlAccessorType(XmlAccessType.PROPERTY)
@XmlType(name="Test")
public class Bean {
private String someValue;
public Bean() {
}
@XmlElement
// @XmlJavaTypeAdapter(NullStringAdapter.class)
public String getSomeValue() {
return someValue == null ? "" : someValue;
}
public void setSomeValue(final String someValue) {
this.someValue = someValue;
}
}
这对我有用。但是,如果您自己创建带有 JAXB 注释的类而不是通过 XJC 从模式中生成它们,这才是真正的选择。
也许有人可以澄清一下为什么适配器会因空值而被跳过,以及是否有办法改变这种行为。