在动态 JSON 中序列化空值和空字符串

2022-09-01 14:49:00

我有这个JSON内容:

{"color":null}

我想用它来制作这些Java对象(反之亦然):

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

我当前的解决方案总是反序列化为空字符串。我找到了其他解决方案,这些解决方案将反序列化或清空字符串为."color":nullnullnull

如何让 MOXy(或任何其他 jaxb 实现)反序列化为空字符串,并将空字符串反序列化为空字符串?nullnull


我正在使用这个代码:

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;
    }
}

它适用于 XML,但不适用于 JSON:

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

我还将 MOXy 配置为相关软件包的 jaxb 提供程序 ():jaxb.properties

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

我正在使用和Java8(但与,,,,的行为相同)。我的妈妈 :MOXy 2.22.12.182.192.202.212.22pom.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>

答案 1

您可能想尝试一些事情。

首先,尝试使用而不是注释或设置参数。@XmlElement(nillable=true)XmlNullPolicyemptyNodeRepresentsNull

其次,你可能想写你自己的,这将做几乎同样的事情 - 将marshall空字符串解为空。主要区别在于,您还可以手动配置哪些字段将与适配器一起取消编组,哪些字段不会,从而保留当前行为。请参阅Blaise Doughan的这个答案,看看如何将自定义连接到您的配置。XmlAdapterXmlAdapter

第三,据我所知,杰克逊没有这个问题。请参阅此wiki页面的答案,了解如何为字段设置自定义反序列化程序,以防万一。


答案 2