How to return a JSON object from a HashMap with Mo

2019-01-08 00:03发布

问题:

I am using Jersey 2.17 with Moxy and I have functions like :

@Produces(APPLICATION_JSON)
@Restricted
public List<User> getFriends(
        @PathParam("user") String user
) {
    return userDAO.getFriends(user);
}

User.preferences is a HashMap.

It works fine for almost all Objects except for a HashMap which gets translated into:

"preferences":{"entry":[{"key":{"type":"string","value":"language"},"value":{"type":"string","value":"en"}},{"key":{"type":"string","value":"country"},"value":{"type":"string","value":"US"}}]}

but what I would really like to return is just a javascript object like:

preferences:{"language":"en","country":"US"}

How can I do that?

回答1:

Yeah MOXy and Maps don't work well. It's sad, as JSON is nothing more than mapped key/value pairs. If you want to use MOXy, you will need to use an XmlAdapter. In which case, the way you want the JSON, you will need to create a type (class) that has the name of all possible preferences. Arbitrary key value pairs should be in the form you require, but since MOXy can't do maps, you'll need to map it to your own type. For instance

public class PreferencesAdapter extends XmlAdapter<Preference, HashMap<String, String>> {

    @XmlRootElement
    public static class Preference {
        public String language;
        public String country;
    }

    @Override
    public HashMap<String, String> unmarshal(Preference p) throws Exception {
        HashMap<String, String> map = new HashMap<>();
        map.put("language", p.language);
        map.put("country", p.country);
        return map;
    }


    @Override
    public Preference marshal(HashMap<String, String> v) throws Exception {
        Preference p = new Preference();
        p.language = v.get("language");
        p.country = v.get("country");
        return p;
    }
}

Your DTO

@XmlRootElement
public class User {
    @XmlJavaTypeAdapter(PreferencesAdapter.class)
    public HashMap<String, String> preferences;
}

But if we're doing to do all this, why don't we just use a Preferences object in the first place instead of a Map? That was a rhetorical question. I totally understand why you wouldn't. But this is one of the limitations of MOXy, as it makes heavy us of JAXB under the hood, and JAXB has never played nicely with Map, which is sad, as like I said, JSON is really nothing more than a Map of key values.

For this reason, and actually many others I have encountered in the past, I do no recommend using MOXy even though it is recommended by Jersey. Instead, use Jackson. Jackson has been the defacto Java goto for JSON processing for a while. For Jackson, just use this dependency

<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>${jersey.version}</version>
</dependency>

If you take out the MOXy dependency, this Jackson module should work out the box. Otherwise if you leave the MOXy dependency, you will need to register the JacksonFeature