Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

JAXB remove XmlRootElement wrapper

I have this @XmlRootElement class Person.

    @XmlRootElement
    class Person {
        private String desc;
    }

and the return content is

    {"Person": {"desc": "abc"} }

and I really don't want the root wrapper, so I want the content to looks like

    {"desc": "abc"}

Can I accomplish this via JaxB? If so, how? Thanks!

like image 314
Thomas Kao Avatar asked Aug 24 '13 10:08

Thomas Kao


2 Answers

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

As answered by esseplymale JAXB (JSR-222) covers XML not JSON binding. Howvever JAXB is frequently used directly (a JAXB impl used with something like Jettison to convert JSON to/from StAX events, see: http://blog.bdoughan.com/2011/04/jaxb-and-json-via-jettison.html) and indirectly (a JSON-binding implementation interpreting a subset of JAXB annotations) in this space.

Below is how you could accomplish your use case by leveraging EclipseLink MOXy as your JAXB provider.

Java Model

Person

Below is the Person class from your question. I have added the @XmlAccessorType(XmlAccessType.FIELD) annotation so that I could avoid adding the accessor methods (see: http://blog.bdoughan.com/2011/06/using-jaxbs-xmlaccessortype-to.html).

import javax.xml.bind.annotation.*;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
class Person {

    private String desc;

}

jaxb.properties

You use 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 (see: http://blog.bdoughan.com/2011/05/specifying-eclipselink-moxy-as-your.html).

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

Demo Code

Demo

There are a few things to note in the demo code:

  1. The JAXBContextProperties.MEDIA_TYPE property is used to put MOXy in JSON-binding mode.
  2. The JAXBContextProperties.JSON_INCLUDE_ROOT property is used to tell MOXy to omit the root element.
  3. When the root element is omitted you need to tell MOXy what type you are unmarshalling by using one of the unmarshal methods that take a Class parameter.
import java.util.*;
import javax.xml.bind.*;
import javax.xml.transform.stream.StreamSource;
import org.eclipse.persistence.jaxb.JAXBContextProperties;

public class Demo {

    public static void main(String[] args) throws Exception {
        Map<String, Object> properties = new HashMap<String, Object>(2);
        properties.put(JAXBContextProperties.MEDIA_TYPE, "application/json");
        properties.put(JAXBContextProperties.JSON_INCLUDE_ROOT, false);
        JAXBContext jc = JAXBContext.newInstance(new Class[] {Person.class}, properties);

        Unmarshaller unmarshaller = jc.createUnmarshaller();
        StreamSource json = new StreamSource("src/forum18417466/input.json");
        Person person = unmarshaller.unmarshal(json, Person.class).getValue();

        Marshaller marshaller = jc.createMarshaller();
        marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
        marshaller.marshal(person, System.out);
    }

}

input.json/Output

{
   "desc" : "abc"
}
like image 88
bdoughan Avatar answered Sep 28 '22 09:09

bdoughan


JAXB is an API for XML, not JSON.

However, there are some JSON libraries (at least Jackson) which can utilize JAXB annotations. I don't know which one you are using, so I don't know exactly how to best help. (If you are using this with the Jersey framework, it uses Jackson for its JSON serialization.) You will need to configure whatever JSON library you are using to be able to configure the "wrapping" of the root element in the JSON output.

I wrote up this quick Groovy script to test this in Jackson:

@Grab('com.fasterxml.jackson.core:jackson-databind:2.2.2')
import javax.xml.bind.annotation.XmlRootElement
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.SerializationFeature

@XmlRootElement
public class Person {
  public String desc = "howdy"
}

Person p = new Person()
ObjectMapper om = new ObjectMapper().enable(SerializationFeature.WRAP_ROOT_VALUE)
println om.writeValueAsString(p)

As it is written above, it outputs the JSON:

{"Person":{"desc":"howdy"}}

If you take out the part .enable(SerializationFeature.WRAP_ROOT_VALUE) it gives you:

{"desc":"howdy"}

So if you are using Jackson, it looks like the ObjectMapper that is being used behind the scenes is being set to WRAP_ROOT_VALUE and you just need to turn that off.

like image 43
jesseplymale Avatar answered Sep 28 '22 08:09

jesseplymale