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都得到妥善处理