使用Jersey / JAXB / Jackson将Java.util.Map转换为JSON对象

26

我一直试图创建一个Jersey REST Web服务。我希望能够从Java类中接收和发出像下面这样的JSON对象:

@XmlRootElement
public class Book {

    public String code;

    public HashMap<String, String> names;

}

应该将它转换为这样的JSON:

{
    "code": "ABC123",
    "names": {
        "de": "Die fabelhafte Welt der Amelie",
        "fr": "Le fabuleux destin d'Amelie Poulain"
    }
}

然而,我找不到这方面的标准解决方案。每个人似乎都在实现自己的包装器解决方案。这个需求对我来说似乎是极其基本的;我无法相信这是普遍接受的解决方案,特别是因为Jersey确实是Java中更有趣的部分之一。

我还尝试升级到Jackson 1.8,但它只给我提供了这个非常难懂的JSON:

{
    "code": "ABC123",
    "names": {
        "entry": [{
            "key": "de",
            "value": "Die fabelhafte Welt der Amelie"
        },
        {
            "key": "fr",
            "value": "Le fabuleux destin d'Amelie Poulain"
        }]
    }
}

有没有任何提出的解决方案?

4个回答

26

我不知道为什么这不是默认设置,而且我花了一些时间才弄清楚,但如果你想在Jersey中实现可用的JSON转换,请添加以下代码:

    <init-param>
        <param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
        <param-value>true</param-value>
    </init-param>

将以下内容添加到您的web.xml文件中,所有问题都应该得到解决。

PS:为使其正常工作,您还需要摆脱@XmlRootElement注释。


+1个小费。但请注意,POJO映射也会在JSON中返回NULL值。我更喜欢JAXB,并使用包含额外POJO的列表。 - H6.
4
这是服务端的设置。要在客户端执行此操作: ClientConfig clientConfig = new DefaultClientConfig(); clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); Client client = Client.create(clientConfig); - Jeremy Brooks
@Jan 如果我需要将_Book_转换为json和xml(使用_accepts_请求),该怎么办? 如果我删除XmlRootElement xml序列化就无法工作了。 - Max Schmidt
1
哇。这本来是一个很好的答案,但无论是否删除@XmlRootElement及其相关注释,并按建议添加到web.xml中,都会出现以下结果:com.sun.jersey.spi.container.ContainerRequest getEntity SEVERE:找不到Java类com.hp.web.user.dto.ProfileDto和Java类型类com.hp.web.user.dto.ProfileDto的消息体读取器,以及MIME媒体类型application/json;charset=UTF-8。 - Russ Bateman
是的,就像Russ所说的那样。这个非解决方案会导致“找不到消息正文编写器”的错误。 - ChrisO
我相信我2012年提供的解决方案是针对Jersey 1.x的 - 或许这个线程中的答案可以帮助处理Jersey 2.x https://dev59.com/3W435IYBdhLWcg3w4UMc - Jan

7
您可以使用 google-gson。以下是示例代码:
    @Test
    public void testGson(){
       Book book = new Book();
       book.code = "1234";
       book.names = new HashMap<String,String>();
       book.names.put("Manish", "Pandit");
       book.names.put("Some","Name");
       String json = new Gson().toJson(book);
       System.out.println(json);
   }

输出为{"code":"1234","names":{"Some":"Name","Manish":"Pandit"}}

1
谢谢,但我特别感兴趣的是与Jersey集成的解决方案。 - samy-delux
我更喜欢使用这种方法来返回复杂的数据结构而不是简单的POJO。它完美地与Jersey集成(您只需在get方法中返回一个“String”),而且您不必在converters的web.xml上进行混乱的设置。 - emas

4

我知道这个问题早就被问过了,但事情在此期间发生了变化,所以对于最新的Jersey v2.22来说,不再有com.sun.jersey包。在项目的pom.xml中添加以下两个依赖项可以解决同样的问题:

<dependency>
  <groupId>org.glassfish.jersey.media</groupId>
  <artifactId>jersey-media-json-jackson</artifactId>
  <version>2.22</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.jaxrs</groupId>
  <artifactId>jackson-jaxrs-json-provider</artifactId>
  <version>2.5.4</version> <!-- jackson version used by jersey v2.22 -->
</dependency>

无需在web.xml中添加任何内容。 第一个依赖项将指示jersey使用jackson进行POJO到JSON转换。 第二个依赖项将注册jackson作为jersey的JSON提供程序。

此外,针对POJO中的null问题,请向POJO类添加此注释:

@JsonInclude(JsonInclude.Include.NON_NULL)

-3
@POST
@Consumes("application/json")
public void createBook(Book book)
{
 .....
 .....
}

当然,你需要为 Book 中的每个属性都有 getter/setter。

另外,使用包装类(通常是一个 map)的推荐原因是避免为想要发送的每种数据创建多个 DTO。只需使用 map 进行序列化/反序列化,并作为业务逻辑的一部分将其转换为相应的 POJO 以进行内部处理,特别是如果你正在使用该 POJO 进行对象关系映射。


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