J'ai besoin d'afficher la valeur null en tant qu'élément vide dans jaxb. J'utilise l'implémentation moxy de jaxb. J'ai trouvé cette option
@XmlNullPolicy(emptyNodeRepresentsNull = true, nullRepresentationForXml = XmlMarshalNullRepresentation.EMPTY_NODE)
Existe-t-il une extension similaire pouvant être appliquée au niveau de la classe (pour tous les éléments définis dans celle-ci)
Je recommanderais fortement de représenter null
avec l'absence du nœud ou avec l'attribut xsi:nil="true"
. Cela fonctionne mieux avec la validation de schéma (c'est-à-dire <age/>
ou <age></age>
n'est pas un élément valide de type xsd:int
. Toutefois, si vous ne pouvez pas, voici comment vous pouvez réaliser votre cas d'utilisation:
COMPORTEMENT JAXB STANDARD
En utilisant les API standard, vous pouvez contrôler si null est représenté en tant que nœud absent ou avec xsi:nil="true"
avec l'annotation @XmlElement
(voir: http://blog.bdoughan.com/2012/04/binding-to-json-xml-handling -null.html ).
import javax.xml.bind.annotation.*;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Address {
private String street;
@XmlElement(nillable=true)
private String city;
}
Vous trouverez ci-dessous la sortie XML si les valeurs des deux champs sont null.
<?xml version="1.0" encoding="UTF-8"?>
<address>
<city xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:nil="true"/>
</address>
MOXY - REMPLACANT CE COMPORTEMENT PAR CLASSE
MOXy ne fournit pas d'annotation pour spécifier la stratégie null pour toutes les propriétés d'une classe. Cependant, vous pouvez exploiter une DescriptorCustomizer
via l'annotation @XmlCustomizer
et ajuster les métadonnées de mappage MOXy natives pour obtenir le même résultat.
DescriptorCustomizer (AddressCustomizer)} _
import org.Eclipse.persistence.config.DescriptorCustomizer;
import org.Eclipse.persistence.descriptors.ClassDescriptor;
import org.Eclipse.persistence.mappings.DatabaseMapping;
import org.Eclipse.persistence.oxm.mappings.XMLDirectMapping;
import org.Eclipse.persistence.oxm.mappings.nullpolicy.XMLNullRepresentationType;
public class AddressCustomizer implements DescriptorCustomizer {
@Override
public void customize(ClassDescriptor descriptor) throws Exception {
for(DatabaseMapping mapping : descriptor.getMappings()) {
if(mapping.isAbstractDirectMapping()) {
XMLDirectMapping xmlDirectMapping = (XMLDirectMapping) mapping;
xmlDirectMapping.getNullPolicy().setMarshalNullRepresentation(XMLNullRepresentationType.EMPTY_NODE);
xmlDirectMapping.getNullPolicy().setNullRepresentedByEmptyNode(true);
}
}
}
}
DomainModel (Address)
import javax.xml.bind.annotation.*;
import org.Eclipse.persistence.oxm.annotations.XmlCustomizer;
@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@XmlCustomizer(AddressCustomizer.class)
public class Address {
private String street;
@XmlElement(nillable=true)
private String city;
}
Sortie
<?xml version="1.0" encoding="UTF-8"?>
<address>
<street/>
<city/>
</address>
MOXY - REMPLACER CE COMPORTEMENT POUR TOUTES LES CLASSES
Si, au lieu de cela, vous souhaitez remplacer la gestion des valeurs null pour toutes les classes mappées, je vous recommanderais plutôt d'utiliser une variable SessionEventListener
. Si vous préférez, vous pouvez également utiliser cette approche pour mettre à jour les métadonnées d'une classe.
SessionEventListener (NullPolicySessionEventListener)} _
import org.Eclipse.persistence.descriptors.ClassDescriptor;
import org.Eclipse.persistence.mappings.DatabaseMapping;
import org.Eclipse.persistence.oxm.mappings.XMLDirectMapping;
import org.Eclipse.persistence.oxm.mappings.nullpolicy.XMLNullRepresentationType;
import org.Eclipse.persistence.sessions.*;
public class NullPolicySessionEventListener extends SessionEventAdapter {
@Override
public void preLogin(SessionEvent event) {
Project project = event.getSession().getProject();
for(ClassDescriptor descriptor : project.getOrderedDescriptors()) {
for(DatabaseMapping mapping : descriptor.getMappings()) {
if(mapping.isAbstractDirectMapping()) {
XMLDirectMapping xmlDirectMapping = (XMLDirectMapping) mapping;
xmlDirectMapping.getNullPolicy().setMarshalNullRepresentation(XMLNullRepresentationType.EMPTY_NODE);
xmlDirectMapping.getNullPolicy().setNullRepresentedByEmptyNode(true);
}
}
}
}
}
Code de démonstration
import Java.util.*;
import javax.xml.bind.*;
import org.Eclipse.persistence.jaxb.JAXBContextProperties;
import org.Eclipse.persistence.sessions.SessionEventListener;
public class Demo {
public static void main(String[] args) throws Exception {
Map<String, Object> properties = new HashMap<String, Object>(1);
SessionEventListener sessionEventListener = new NullPolicySessionEventListener();
properties.put(JAXBContextProperties.SESSION_EVENT_LISTENER, sessionEventListener);
JAXBContext jc = JAXBContext.newInstance(new Class[] {Address.class}, properties);
Address address = new Address();
Marshaller marshaller = jc.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(address, System.out);
}
}
Sortie
<?xml version="1.0" encoding="UTF-8"?>
<address>
<street/>
<city/>
</address>
Une solution de contournement "mauvaise pratique" si vous n'avez que des champs String dans la classe consiste à remplacer le créateur de l'élément comme ceci:
public class Address {
private String street;
@XmlElement(name = "street")
public void setStreet(String street){
this.street = street;
if (this.street == null){
this.street = ""; // Not NULL, empty string like new String()!
}
}
}
Cela ne fonctionnera pas avec d'autres types comme la date ou le numéro! Mais parfois, String est suffisant.
La réponse de @Blaise Doughan semble bien meilleure à long terme si vous pouvez vous en occuper. :)