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>
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With