Конечно, адаптер никогда не будет вызван, если на входе нет элемента, который бы инициировал это действие. В этом примере, с которым вы связаны, происходит то, что представлен атрибут с пустым значением:
<element att="" />
Ключевым моментом здесь является то, что является атрибутом att
, но имеет пустую строку. Таким образом, демаршаллер JAXB представит это сеттеру. Но, поскольку на нем объявлен адаптер, он пройдет через него и получит нулевое значение.
Но если бы у вас было это
<element />
это другая история. Нет атрибута att
, поэтому никогда не потребуется вызывать сеттер.
Существует разница между элементом, который возникает, но не имеет содержимого, и полным отсутствием элемента. Первый может в основном рассматриваться как содержащий пустую строку, но последний просто «не существует».
РЕДАКТИРОВАТЬ: испытания с этими классами ...
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>
На самом деле меня немного удивило. Затем я попытался изменить геттер на return someValue == null ? "" : someValue;
безрезультатно. Затем установите точку останова на получателе и обнаружите, что она никогда не вызывается.
Очевидно, JAXB использует отражение, чтобы попытаться получить значение, а не проходить через установщик при использовании XmlAccessType.FIELD
. Hardcore. Теперь вы можете обойти это, используя вместо этого 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;
}
public void setSomeValue(final String someValue) {
this.someValue = someValue;
}
}
... но это все равно не помогло. Метод marshal
адаптера вызывался только один раз, в последнем тестовом примере, где была задана пустая строка. Очевидно, он сначала вызывает метод получения, а когда он возвращает значение NULL, он просто полностью пропускает содержимое адаптера.
Единственное решение, которое я могу придумать, - это просто полностью отказаться от использования адаптера здесь и поместить подстановку в геттер, убедившись, что используется 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 из схемы.
Может быть, кто-то может немного уточнить, почему адаптеры пропускаются для нулей и есть ли способ изменить это поведение.