Search code examples
javajsonjaxbmarshallingjettison

marshalling java object to JSON adds empty property using JAX-RS


I'm using jax-rs and adding entity for being marshalled in Jetty by JAXRSOutInterceptor, but JSON output is being modified with additional empty property which looks like this: "$": ""

The JSONProvider is created and configured as here:

JSONProvider jsonProvider = new JSONProvider();
jsonProvider.setConvertTypesToStrings(true);
jsonProvider.setIgnoreNamespaces(true);
jsonProvider.setIgnoreMixedContent(true);
jsonProvider.setUnmarshallAsJaxbElement(true);
providers.add(jsonProvider);

It is also being marshalled to XML which uses namespaces but I don't want them in JSON output and input.

The object that is being marshalled is similar to this:

@XmlRootElement(name="myObject1")
@XmlAccessorType(XmlAccessType.FIELD)
@SuppressWarnings("serial")
public class MyObject1 implements Serializable {

    MyObject2 a;
    MyObject2 b;
    MyObject2 c;

// includes getters, setters, hashCode, equals, toString,   
}

When MyObject2 is:

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
@SuppressWarnings("serial")
public class MyObject2 implements Serializable {

    String x;
    String y;
    List<String> z;

// includes getters, setters, hashCode, equals, toString,   
}

The rest output is as following:

{
   "myObject1": {
      "a": {
         "x": "value1",
         "y": "value2",
         "z": "value3",
         "$": ""
      },
      "$": ""
   }
}

How do I get rid of the ending "$": "" I read that the Jettison (which is the default JSONProvider implementation that I am using) is by default will represent properties mapped with @XmlValue as '$'s but there's no property ?

Is that caused by implementing Serializable?


Solution

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

    Jettison isn't really a JSON provider but an API that can allow XML libraries to produce/consume JSON. It does a decent job, but there are problems that can occur like the one you are experiencing now. You are also seeing the issues where lists of size one are not marshalled as JSON arrays.

    If you can't find a way to make this work with your current set up. Below is what you could do with MOXy as your JSON provider:

    MyObject1

    package forum11262807;
    
    import java.io.Serializable;
    import javax.xml.bind.annotation.*;
    
    @XmlRootElement(name="myObject1")
    @XmlAccessorType(XmlAccessType.FIELD)
    @SuppressWarnings("serial")
    public class MyObject1 implements Serializable {
    
        MyObject2 a;
        MyObject2 b;
        MyObject2 c;
    
    }
    

    MyObject2

    package forum11262807;
    
    import java.io.Serializable;
    import java.util.List;
    import javax.xml.bind.annotation.*;
    
    @XmlAccessorType(XmlAccessType.FIELD)
    @SuppressWarnings("serial")
    public class MyObject2 implements Serializable {
    
        String x;
        String y;
        List<String> z;
    
    }
    

    jaxb.properties

    To specify MOXy as your JAXB provider you need to include a file called jaxb.properties in the same package as your domain model with the following entry:

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

    Demo

    Below is some standalone code to demonstrate the reading/writing of the JSON.

    package forum11262807;
    
    import java.io.File;
    import javax.xml.bind.*;
    
    public class Demo {
    
        public static void main(String[] args) throws Exception {
            JAXBContext jc = JAXBContext.newInstance(MyObject1.class);
    
            Unmarshaller unmarshaller = jc.createUnmarshaller();
            unmarshaller.setProperty("eclipselink.media-type", "application/json");
            File json = new File("src/forum11262807/input.json");
            MyObject1 myObject1 = (MyObject1) unmarshaller.unmarshal(json);
    
            Marshaller marshaller = jc.createMarshaller();
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
            marshaller.setProperty("eclipselink.media-type", "application/json");
            marshaller.marshal(myObject1, System.out);
        }
    
    }
    

    input.json/Output

    The resulting JSON message contains no "$" properties and the list of size 1 is property represented as a JSON array.

    {
       "myObject1" : {
          "a" : {
             "x" : "value1",
             "y" : "value2",
             "z" : [ "value3" ]
          }
       }
    }
    

    JAX-RS Integration

    MOXy includes the MOXyJsonProvider class that makes it easy to configure in your JAX-RS application: