Moxy JAXB和“失踪”之间的混淆'与显式空值

时间:2016-08-05 01:00:06

标签: xml jaxb moxy

我真的需要能够区分&#39;缺少&#39;和&#39; null&#39;将XML解组为POJO时。我有一个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;
    }
}

我想要的是缺少这个Optional<BigInteger>字段的节点的XML没有调用setter,但是对于任何有空节点的XML(我选择它来表示显式null)调用将字段设置为Optional.empty()的setter。

如果我这样做,显式null的情况不起作用:

@XmlNullPolicy(emptyNodeRepresentsNull = true, nullRepresentationForXml = XmlMarshalNullRepresentation.EMPTY_NODE, 
        isSetPerformedForAbsentNode =false)
private Optional<BigInteger> field;

该字段未设置且仍为null。如果我将isSetPerformedForAbsentNode设置为true,则缺少节点的情况不起作用。使用null调用setter,并将字段设置为Optional.empty()。有什么办法,我可以配置一些JAXB实现到我想要的东西?缺席和null意味着非常不同的事情,我需要能够分辨出来。

1 个答案:

答案 0 :(得分:2)

我必须使用元素引用和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适配器类:

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中的吸尘器和制定者:

@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;
}

现在我所有的单元测试都通过了。非null,null和缺失都得到了妥善处理。