Moxy JAXB 和区分 'missing' 与显式空值
Moxy JAXB and distringuishing between 'missing' versus explicit nulls
在将 XML 解组为 POJO 时,我真的需要能够区分 'missing' 和 'null'。我有一个 Optional<BigInteger>
字段和一个用于 Optional
类型的适配器:
public abstract class OptionalAdapter<T> extends XmlAdapter<T, Optional<T>> {
@Override
public Optional<T> unmarshal(T value) throws Exception {
log.debug("Unmarshalling value: {}", value);
if(value == null) {
log.debug("Value is null, returning Optional.empty()");
return Optional.empty();
} else {
log.debug("Value is not null, returning an optional holding the value");
return Optional.of(value);
}
}
@Override
public T marshal(Optional<T> value) throws Exception {
if (value == null) {
return null;
}
return value.isPresent() ? value.get() : null;
}
}
我想要的是 XML 缺少此 Optional<BigInteger>
字段的节点不调用 setter,但对于任何 XML 确实有一个空节点(我选择它来表示显式 null)来调用 setter,这会将字段设置为 Optional.empty()
。
如果我这样做,显式 null 的情况将不起作用:
@XmlNullPolicy(emptyNodeRepresentsNull = true, nullRepresentationForXml = XmlMarshalNullRepresentation.EMPTY_NODE,
isSetPerformedForAbsentNode =false)
private Optional<BigInteger> field;
该字段未设置并保持 null
。如果我将 isSetPerformedForAbsentNode
设置为 true
,则缺少节点的情况不起作用。使用 null
调用 setter,字段设置为 Optional.empty()
。有什么办法,我可以将 JAXB 的一些实现配置为我想要的吗?缺席和 null
意味着非常不同的事情,我需要能够区分。
我不得不使用元素引用和 JaxB 的 Moxy 实现。
我将 jaxb.properties
文件放入我的 POJO 的包中,其中 属性 定义为:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
然后我声明了两个字段,Optional<BigInteger>
字段是 POJO 上 属性 的实际值,还有一个元素引用字段,我用它来确定该值是否显式为 null 或XML 来源中缺失。
private Optional<BigInteger> parentGroupId;
private JAXBElement<BigInteger> parentGroupIdElementRef;
我的 Optional
适配器 class:
public abstract class OptionalAdapter<T> extends XmlAdapter<T, Optional<T>> {
@Override
public Optional<T> unmarshal(T value) throws Exception {
return Optional.of(value);
}
@Override
public T marshal(Optional<T> value) throws Exception {
return value.isPresent() ? value.get() : null;
}
}
我的元素引用处理程序:
@XmlRegistry
public class ParentGroupIdXmlElementRef {
@XmlElementDecl(name="parentGroupId")
public JAXBElement<BigInteger> createFooJAXBElement(final BigInteger value) {
return new JAXBElement<>(new QName("parentGroupId"), BigInteger.class, value);
}
}
我在 POJO 中的 getter 和 setter:
@XmlElement(required = false, nillable = true)
@XmlJavaTypeAdapter(OptionalBigIntegerAdapter.class)
@XmlNullPolicy(isSetPerformedForAbsentNode = false, nullRepresentationForXml = XmlMarshalNullRepresentation.XSI_NIL)
@Override
public void setParentGroupId(final Optional<BigInteger> parent) {
log.debug("Parent setter called: {}", parent);
if (parent != null && parent.isPresent() && parent.get().signum() == -1) {
throw log.throwing(new IllegalArgumentException("Cannot specify a parent group ID less than 0"));
}
this.parentGroupId = parent;
//this.isParentIdMissing = false;
if (parent == null) {
parentGroupIdElementRef = null;
} else if (parent.isPresent() && parentGroupIdElementRef == null) {
parentGroupIdElementRef = new ParentGroupIdXmlElementRef().createFooJAXBElement(parent.get());
} else if(parentGroupIdElementRef == null) {
parentGroupIdElementRef = new ParentGroupIdXmlElementRef().createFooJAXBElement(null);
parentGroupIdElementRef.setNil(true);
}
}
@XmlElement(required = false, nillable = true)
@XmlJavaTypeAdapter(OptionalBigIntegerAdapter.class)
@XmlNullPolicy(isSetPerformedForAbsentNode = false, nullRepresentationForXml = XmlMarshalNullRepresentation.XSI_NIL)
@Override
public @Nullable @Nonnegative Optional<BigInteger> getParentGroupId() {
return parentGroupId;
}
@XmlElementRef(name="parentGroupId", required=false)
public void setParentGroupIdElementRef(final JAXBElement<BigInteger> elementRef) {
log.debug("Setting element reference for parent ID: {}", elementRef);
this.parentGroupIdElementRef = elementRef;
if(parentGroupIdElementRef == null) {
setParentGroupId(null);
} else if(parentGroupIdElementRef.isNil()) {
setParentGroupId(Optional.empty());
} else {
setParentGroupId(Optional.of(elementRef.getValue()));
}
}
@XmlElementRef(name="parentGroupId", required=false)
public JAXBElement<BigInteger> getParentGroupIdElementRef() {
log.debug("Getting Element reference: {}", parentGroupIdElementRef);
return this.parentGroupIdElementRef;
}
现在我所有的单元测试都通过了。 Non-null、null、missing都得到妥善处理
在将 XML 解组为 POJO 时,我真的需要能够区分 'missing' 和 'null'。我有一个 Optional<BigInteger>
字段和一个用于 Optional
类型的适配器:
public abstract class OptionalAdapter<T> extends XmlAdapter<T, Optional<T>> {
@Override
public Optional<T> unmarshal(T value) throws Exception {
log.debug("Unmarshalling value: {}", value);
if(value == null) {
log.debug("Value is null, returning Optional.empty()");
return Optional.empty();
} else {
log.debug("Value is not null, returning an optional holding the value");
return Optional.of(value);
}
}
@Override
public T marshal(Optional<T> value) throws Exception {
if (value == null) {
return null;
}
return value.isPresent() ? value.get() : null;
}
}
我想要的是 XML 缺少此 Optional<BigInteger>
字段的节点不调用 setter,但对于任何 XML 确实有一个空节点(我选择它来表示显式 null)来调用 setter,这会将字段设置为 Optional.empty()
。
如果我这样做,显式 null 的情况将不起作用:
@XmlNullPolicy(emptyNodeRepresentsNull = true, nullRepresentationForXml = XmlMarshalNullRepresentation.EMPTY_NODE,
isSetPerformedForAbsentNode =false)
private Optional<BigInteger> field;
该字段未设置并保持 null
。如果我将 isSetPerformedForAbsentNode
设置为 true
,则缺少节点的情况不起作用。使用 null
调用 setter,字段设置为 Optional.empty()
。有什么办法,我可以将 JAXB 的一些实现配置为我想要的吗?缺席和 null
意味着非常不同的事情,我需要能够区分。
我不得不使用元素引用和 JaxB 的 Moxy 实现。
我将 jaxb.properties
文件放入我的 POJO 的包中,其中 属性 定义为:
javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory
然后我声明了两个字段,Optional<BigInteger>
字段是 POJO 上 属性 的实际值,还有一个元素引用字段,我用它来确定该值是否显式为 null 或XML 来源中缺失。
private Optional<BigInteger> parentGroupId;
private JAXBElement<BigInteger> parentGroupIdElementRef;
我的 Optional
适配器 class:
public abstract class OptionalAdapter<T> extends XmlAdapter<T, Optional<T>> {
@Override
public Optional<T> unmarshal(T value) throws Exception {
return Optional.of(value);
}
@Override
public T marshal(Optional<T> value) throws Exception {
return value.isPresent() ? value.get() : null;
}
}
我的元素引用处理程序:
@XmlRegistry
public class ParentGroupIdXmlElementRef {
@XmlElementDecl(name="parentGroupId")
public JAXBElement<BigInteger> createFooJAXBElement(final BigInteger value) {
return new JAXBElement<>(new QName("parentGroupId"), BigInteger.class, value);
}
}
我在 POJO 中的 getter 和 setter:
@XmlElement(required = false, nillable = true)
@XmlJavaTypeAdapter(OptionalBigIntegerAdapter.class)
@XmlNullPolicy(isSetPerformedForAbsentNode = false, nullRepresentationForXml = XmlMarshalNullRepresentation.XSI_NIL)
@Override
public void setParentGroupId(final Optional<BigInteger> parent) {
log.debug("Parent setter called: {}", parent);
if (parent != null && parent.isPresent() && parent.get().signum() == -1) {
throw log.throwing(new IllegalArgumentException("Cannot specify a parent group ID less than 0"));
}
this.parentGroupId = parent;
//this.isParentIdMissing = false;
if (parent == null) {
parentGroupIdElementRef = null;
} else if (parent.isPresent() && parentGroupIdElementRef == null) {
parentGroupIdElementRef = new ParentGroupIdXmlElementRef().createFooJAXBElement(parent.get());
} else if(parentGroupIdElementRef == null) {
parentGroupIdElementRef = new ParentGroupIdXmlElementRef().createFooJAXBElement(null);
parentGroupIdElementRef.setNil(true);
}
}
@XmlElement(required = false, nillable = true)
@XmlJavaTypeAdapter(OptionalBigIntegerAdapter.class)
@XmlNullPolicy(isSetPerformedForAbsentNode = false, nullRepresentationForXml = XmlMarshalNullRepresentation.XSI_NIL)
@Override
public @Nullable @Nonnegative Optional<BigInteger> getParentGroupId() {
return parentGroupId;
}
@XmlElementRef(name="parentGroupId", required=false)
public void setParentGroupIdElementRef(final JAXBElement<BigInteger> elementRef) {
log.debug("Setting element reference for parent ID: {}", elementRef);
this.parentGroupIdElementRef = elementRef;
if(parentGroupIdElementRef == null) {
setParentGroupId(null);
} else if(parentGroupIdElementRef.isNil()) {
setParentGroupId(Optional.empty());
} else {
setParentGroupId(Optional.of(elementRef.getValue()));
}
}
@XmlElementRef(name="parentGroupId", required=false)
public JAXBElement<BigInteger> getParentGroupIdElementRef() {
log.debug("Getting Element reference: {}", parentGroupIdElementRef);
return this.parentGroupIdElementRef;
}
现在我所有的单元测试都通过了。 Non-null、null、missing都得到妥善处理