web-dev-qa-db-fra.com

Représenter la valeur NULL en tant qu'élément vide dans xml jaxb

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)

11
Anand B

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>
18
Blaise Doughan

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. :)

0
Laszlo Lugosi