28 votes

Sérialisation des valeurs nulles et des chaînes vides dans le JSON dynamique

J'ai ce contenu JSON :

{"color":null}

Et je veux en faire des objets Java (et vice-versa) :

Container
|- List<Entry> entries
   |- Entry
      |- String key = "color"
      |- String value = null

Ma solution actuelle désérialise toujours "color":null à un empty String . J'ai trouvé d'autres solutions, qui vont plutôt désérialiser null ou une chaîne vide pour null .

Comment faire pour que MOXy (ou toute autre implémentation de jaxb) désérialise null comme null et des chaînes vides vers des chaînes vides ?


J'utilise ce code :

import java.io.ByteArrayOutputStream;
import java.util.*;

import javax.xml.bind.*;
import javax.xml.bind.annotation.*;

import org.eclipse.persistence.internal.oxm.ByteArraySource;
import org.eclipse.persistence.jaxb.JAXBContextProperties;
import org.eclipse.persistence.oxm.annotations.*;
import org.junit.*;

class Container {

    @XmlVariableNode("key")
    List<Entry> entries = new ArrayList<Entry>();   
}

class Entry {

    @XmlTransient
    public String key;

    @XmlValue
    @XmlNullPolicy(nullRepresentationForXml=XmlMarshalNullRepresentation.XSI_NIL, xsiNilRepresentsNull=false)
    public String value;
}

public class D {

    /** THIS TEST FAILS!!! */

    @Test
    public void unmarshallNull() throws Exception {
        Assert.assertEquals(null, unmarshall("xml", "<root><color xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:nil=\"true\"/></root>"));
        Assert.assertEquals(null, unmarshall("json", "{\"color\":null}"));
    }

    /* All other tests are passing. */

    @Test
    public void unmarshallEmpty() throws Exception {
        Assert.assertEquals("", unmarshall("xml", "<root><color></color></root>"));
        Assert.assertEquals("", unmarshall("json", "{\"color\":\"\"}"));
    }

    @Test
    public void unmarshallValue() throws Exception {
        Assert.assertEquals("red", unmarshall("xml", "<root><color>red</color></root>"));
        Assert.assertEquals("red", unmarshall("json", "{\"color\":\"red\"}"));
    }

    @Test
    public void marshallNull() throws Exception {
        Assert.assertEquals("<color xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xsi:nil=\"true\"/>", marshall("xml", null));
        Assert.assertEquals("{\"color\":null}", marshall("json", null));
    }

    @Test
    public void marshallEmpty() throws Exception {
        Assert.assertEquals("<color></color>", marshall("xml", ""));
        Assert.assertEquals("{\"color\":\"\"}", marshall("json", ""));
    }

    @Test
    public void marshallValue() throws Exception {
        Assert.assertEquals("<color>red</color>", marshall("xml", "red"));
        Assert.assertEquals("{\"color\":\"red\"}", marshall("json", "red"));
    }

    private static String marshall(String format, String value) throws JAXBException {

        // prepare
        JAXBContext jc = JAXBContext.newInstance(Container.class);
        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(JAXBContextProperties.MEDIA_TYPE, "application/"+format);
        marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
        marshaller.setProperty(JAXBContextProperties.JSON_INCLUDE_ROOT, false);

        // define example data
        Container detail = new Container();
        Entry entry = new Entry();
        entry.key = "color";
        entry.value = value;
        detail.entries.add(entry);

        // marshall
        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
        marshaller.marshal(detail, outputStream);
        return outputStream.toString();
    }

    private static String unmarshall(String format, String raw) throws JAXBException {

        // prepare
        JAXBContext jc = JAXBContext.newInstance(Container.class);
        Unmarshaller unmarshaller = jc.createUnmarshaller();
        unmarshaller.setProperty(JAXBContextProperties.MEDIA_TYPE, "application/"+format);
        unmarshaller.setProperty(JAXBContextProperties.JSON_INCLUDE_ROOT, false);

        // unmarshall
        Container container = unmarshaller.unmarshal(new ByteArraySource(raw.getBytes()), Container.class).getValue();
        return container.entries.get(0).value;
    }
}

Cela fonctionne pour XML mais échoue pour JSON :

Test failure: unmarshallNull
java.lang.AssertionError: expected:<null> but was:<>

J'ai également configuré MOXy comme mon fournisseur de jaxb pour le paquet pertinent ( jaxb.properties ):

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

J'utilise MOXy 2.22.1 et Java8 (mais même comportement avec 2.18 , 2.19 , 2.20 , 2.21 , 2.22 ). Mon Maven pom.xml :

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>x</groupId>
    <artifactId>x</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <dependencies>
        <dependency>
            <groupId>org.glassfish.jersey.media</groupId>
            <artifactId>jersey-media-moxy</artifactId>
            <version>2.22.1</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

5voto

Jeor Mattan Points 390

Il y a des choses que vous pourriez essayer.

Tout d'abord, essayez d'utiliser @XmlElement(nillable=true) au lieu de XmlNullPolicy ou la mise en place de l'annotation emptyNodeRepresentsNull paramètre.

Deuxièmement, vous pouvez écrire votre propre XmlAdapter qui fera à peu près la même chose - démarshaller une chaîne vide en null. La principale différence est que vous pouvez également configurer manuellement les champs qui seront désarchivés avec votre adaptateur et ceux qui ne le seront pas, conservant ainsi le comportement actuel. Veuillez vous référer à ce qui suit responder par Blaise Doughan pour voir comment câbler un appareil personnalisé XmlAdapter à votre configuration.

Troisièmement, pour autant que je sache, Jackson n'a pas ce problème. Voir cette réponse ot cette page wiki sur la façon de configurer un désérialiseur personnalisé pour un champ au cas où cela se produirait.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X