2016-08-05 47 views
0

Ich muss wirklich 'fehlende' und 'null' unterscheiden können, wenn Sie XML in ein POJO entpacken. Ich habe ein Optional<BigInteger> Feld und einen Adapter für Optional Typen:Moxy JAXB und distinguishing zwischen 'fehlende' versus explizite Nullen

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

Was ich will, für XML ist, die einen Knoten für dieses Optional<BigInteger> Feld fehlt nicht der Setter nennen, aber für jede XML, die einen Knoten hat, mit welchen ist leer (ich habe dies gewählt, um eine explizite Null darzustellen), um den Setter aufzurufen, der das Feld auf Optional.empty() setzt.

Wenn ich dies tun, wird der Fall für explizite null nicht:

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

Das Feld ist nicht festgelegt und bleibt null. Wenn ich isSetPerformedForAbsentNode auf true einstelle, funktioniert der Fall für fehlende Knoten nicht. Der Setzer wird mit null aufgerufen und das Feld wird auf Optional.empty() gesetzt. Gibt es eine Möglichkeit, kann ich einige Implementierung von JAXB zu was ich will konfigurieren? Abwesend und null bedeuten sehr unterschiedliche Dinge und ich muss in der Lage sein, den Unterschied zu erkennen.

Antwort

1

Ich musste eine Elementreferenz und die Moxy-Implementierung von JaxB verwenden.

Ich legte die eine jaxb.properties Datei im Paket für meine POJO mit dieser Eigenschaft definiert:

javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory 

Dann erklärte ich zwei Felder, das Optional<BigInteger> Feld, das der tatsächliche Wert der Immobilie auf der POJO ist und einem Elementreferenzfeld, mit dem ich feststellen kann, ob der Wert explizit null ist oder in der XML-Quelle fehlt.

private Optional<BigInteger> parentGroupId; 
private JAXBElement<BigInteger> parentGroupIdElementRef; 

Mein Optional Adapterklasse:

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

Mein Handler für das Element Referenz:

@XmlRegistry 
public class ParentGroupIdXmlElementRef { 

    @XmlElementDecl(name="parentGroupId") 
    public JAXBElement<BigInteger> createFooJAXBElement(final BigInteger value) { 
     return new JAXBElement<>(new QName("parentGroupId"), BigInteger.class, value); 
    } 
} 

Meine Getter und Setter in der 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; 
} 

Jetzt alle meine Einheit Tests bestehen. Nicht-null, null und fehlende werden alle entsprechend behandelt.