如何使用Moxy和Jersey从HashMap返回JSON对象

4
我正在使用带有Moxy的Jersey 2.17,我有以下功能:
@Produces(APPLICATION_JSON)
@Restricted
public List<User> getFriends(
        @PathParam("user") String user
) {
    return userDAO.getFriends(user);
}

User.preferences是一个HashMap

对于几乎所有的对象,它都可以正常工作,除了HashMap,它会被翻译成:

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

但我真正想返回的只是一个JavaScript对象,如下所示:

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

我该怎么做呢?

1个回答

9

很遗憾,MOXy和Maps不太兼容。因为JSON只是映射键值对。如果您想使用MOXy,则需要使用XmlAdapter。在这种情况下,您需要创建一个类型(类),该类型具有所有可能的首选项名称。任意键值对应该以您所需的形式存在,但由于MOXy无法处理maps,您需要将其映射到自己的类型。例如:

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

您的数据传输对象(DTO)
@XmlRootElement
public class User {
    @XmlJavaTypeAdapter(PreferencesAdapter.class)
    public HashMap<String, String> preferences;
}

但是,如果我们要做所有这些事情,为什么不一开始就使用Preferences对象而不是Map呢?那是一个修辞性问题。我完全理解为什么你不会这样做。但这是MOXy的局限之一,因为它在底层大量使用了JAXB,并且JAXB从来没有很好地处理Map,这很让人沮丧,因为正如我所说,JSON实际上就是一个键值对的Map。
出于这个原因,以及我过去遇到的许多其他原因,我不建议使用MOXy,即使它被Jersey推荐。相反,请使用Jackson。Jackson一直是Java中处理JSON的首选工具。对于Jackson,只需使用此依赖项。
<dependency>
    <groupId>org.glassfish.jersey.media</groupId>
    <artifactId>jersey-media-json-jackson</artifactId>
    <version>${jersey.version}</version>
</dependency>

如果您删除了MOXy依赖项,此Jackson模块应该能够直接使用。否则,如果保留MOXy依赖项,您需要注册JacksonFeature

我想使用地图的原因是我在数据库中持久化对象时没有使用ORM,因此我需要将JSONObjects转换为字符串,然后再进行解组,而地图可以直接使用。 - arisalexis
它可以正常工作且开箱即用。在此处编写可能有助于其他用户:Jackson 也会序列化 null 值,并且资源需要使用 @Produces(APPLICATION_JSON) 进行显式注释。 - arisalexis
1
如果您想使用Jackson排除空字段,可以在类上注释@JsonInclude(JsonInclude.Include.NON_NULL),或者不必注释所有类,您可以在ContextResolver配置ObjectMapper - Paul Samsotha
1
+1,因为您仔细阅读整个示例,并得出了Jackson更适合此情况的合理结论。我最近几天一直在与MOXy和Maps进行斗争,而您让我省去了很多麻烦。 - agmangas
如果事先不知道密钥,有什么建议可以使其工作?例如,密钥可以是任意字符串。 - Zoltán Umlauf
@ZoltánUmlauf https://gist.github.com/psamsotha/624d0dc1cc8a00c417e57f8fce8d905d。我刚试了一下,使用Jackson处理Map似乎不起作用。这就是为什么我建议使用Jackson的转换器版本,即JsonSerializer、JsonDeserializer。 - Paul Samsotha

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接