2016-08-05 1 views
0

Je dois vraiment pouvoir faire la distinction entre 'manquant' et 'nul' lors de la conversion d'un fichier XML en POJO. J'ai un champ Optional<BigInteger> et un adaptateur pour Optional types:Moxy JAXB et la distinction entre les 'manquants' et les nulls explicites

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

Ce que je veux XML qui manque un nœud pour ce champ Optional<BigInteger> appeler non pas le compositeur, mais pour tout XML qui a un noeud qui est vide (j'ai choisi ceci pour représenter une valeur nulle explicite) pour appeler le setter qui définira le champ à Optional.empty().

Si je fais cela, le cas pour nulle explicite ne fonctionne pas:

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

Le champ n'est pas défini et reste null. Si je mets isSetPerformedForAbsentNode à true, alors le cas du noeud manquant ne fonctionne pas. Le setter est appelé avec null et le champ est défini sur Optional.empty(). Y at-il un moyen, je peux configurer une certaine mise en œuvre de JAXB à ce que je veux? Absent et null signifient des choses très différentes et je dois être capable de faire la différence.

Répondre

1

J'ai dû utiliser une référence d'élément et l'implémentation Moxy de JaxB.

J'ai placé le fichier jaxb.properties dans le package pour mon POJO avec cette propriété définie:

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

Je déclarai deux champs, le champ Optional<BigInteger> qui est la valeur réelle de la propriété sur le POJO et une champ de référence d'élément que j'utilise pour déterminer si la valeur est explicitement nulle ou manquante dans la source XML.

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

Mon Optional classe d'adaptateur:

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

Mon gestionnaire pour la référence de l'élément:

@XmlRegistry 
public class ParentGroupIdXmlElementRef { 

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

Mes accesseurs dans le 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; 
} 

Maintenant mon unité les tests passent. Non-nul, nul et manquant sont tous traités de manière appropriée.