JAXB remove XmlRootElement wrapper

2020-06-18 07:07发布

问题:

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!

回答1:

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.



回答2:

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