Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Serializing nulls and empty Strings in dynamic JSON

I have this JSON-Content:

{"color":null} 

And I want to make these Java-Objects out of it (and vice-versa):

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

My current solution always deserializes "color":null to an empty String. I found other solutions, that will instead deserialize null or empty String to null.

How can I get MOXy (or any other jaxb implementation) to deserialize null as null and empty Strings to empty Strings?


I am using this 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;     } } 

It works for XML but fails for JSON:

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

I also configured MOXy as my jaxb-provider for the relevant package (jaxb.properties):

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

I'm using MOXy 2.22.1 and Java8 (but same behaviour with 2.18, 2.19, 2.20, 2.21, 2.22). My 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> 
like image 739
slartidan Avatar asked Dec 15 '15 13:12

slartidan


1 Answers

There are things you might want to try.

First, try using @XmlElement(nillable=true) instead of XmlNullPolicy annotation or setting the emptyNodeRepresentsNull parameter.

Second, you might want to write your own XmlAdapter which will do pretty much the same thing - unmarshall empty string to null. The main difference is that you can also manually configure which fields will be unmarshalled with your adapter and which will not, therefore retaining the current behaviour. Please refer to this answer by Blaise Doughan to see how do you wire a custom XmlAdapter to your configuration.

Third, as far as I know Jackson doesn't have this problem. See this answer ot this wiki page about how to set up a custom deserializer for a field in case it does.

like image 74
Jeor Mattan Avatar answered Sep 30 '22 12:09

Jeor Mattan