JSON marshaling of object arrays with JAXB

3k views Asked by At

Using MOXy I'm trying to marshal a java class like this to JSON:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Request {
   String method;
   @XmlAnyElement(lax=true)
   Object[] arguments;
}

I would expect something like:

{
    "method": "test",
    "arguments": ["a", "b"]
}

but the JSON output results to:

{
    "method": "test",
    "value": ["a", "b"]
}

Where is the value coming from?

If I put a @XmlElementWrapper over the arguments field, it gets even worse:

{
    "method":"test",
    "arguments":"a""value":["b"]
}

My JUnit TestCase looks like this:

import static org.junit.Assert.assertEquals;

import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

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

import org.junit.Test;

public class JsonRequestTest {

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public static class Request {
    String method;

    @XmlAnyElement(lax=true)
    Object[] arguments;
} // InvocationRequest

@Test
public void testObjectArray() throws JAXBException {
    System.setProperty(JAXBContext.class.getName(), "org.eclipse.persistence.jaxb.JAXBContextFactory");
    Map<String, Object> props= new HashMap<String, Object>();
    props.put("eclipselink.media-type", "application/json");
    props.put("eclipselink.json.include-root", false);

    JAXBContext ctx = JAXBContext.newInstance(new Class<?>[]{Request.class},props);
    Marshaller m = ctx.createMarshaller();
    StringWriter writer = new StringWriter();
    Request req = new Request(); 
    req.method="test";
    req.arguments = new Object[]{"a","b"};
    m.marshal(req, writer);
    assertEquals("{\"method\":\"test\", \"arguments\":[\"a\",\"b\"]}", writer.toString());
}
} // class JsonRequestTest
1

There are 1 answers

5
bdoughan On

Note: I'm the EclipseLink MOXy lead and a member of the JAXB (JSR-222) expert group.

TL:DR

You can set the following property to override the value key.

    props.put(MarshallerProperties.JSON_VALUE_WRAPPER, "arguments");

Full Test Case

Below is the full working test case. In addition to setting the property I removed an extra space you had in your control document to get the test to pass.

import static org.junit.Assert.assertEquals;

import java.io.StringWriter;
import java.util.HashMap;
import java.util.Map;

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

import org.eclipse.persistence.jaxb.MarshallerProperties;
import org.junit.Test;

public class JsonRequestTest {

    @XmlRootElement
    @XmlAccessorType(XmlAccessType.FIELD)
    public static class Request {
        String method;

        @XmlAnyElement(lax = true)
        Object[] arguments;
    } // InvocationRequest

    @Test
    public void testObjectArray() throws JAXBException {
        System.setProperty(JAXBContext.class.getName(),
                "org.eclipse.persistence.jaxb.JAXBContextFactory");
        Map<String, Object> props = new HashMap<String, Object>();
        props.put("eclipselink.media-type", "application/json");
        props.put("eclipselink.json.include-root", false);
        props.put(MarshallerProperties.JSON_VALUE_WRAPPER, "arguments");

        JAXBContext ctx = JAXBContext.newInstance(
                new Class<?>[] { Request.class }, props);
        Marshaller m = ctx.createMarshaller();
        StringWriter writer = new StringWriter();
        Request req = new Request();
        req.method = "test";
        req.arguments = new Object[] { "a", "b" };
        m.marshal(req, writer);
        assertEquals("{\"method\":\"test\",\"arguments\":[\"a\",\"b\"]}",
                writer.toString());
    }

} // class JsonRequestTest

@XmlElementWrapper Issue

I have opened the following bug for the issue with @XmlElementWrapper: