26

I've been trying to create a Jersey REST Webservice. I want to receive and emit JSON objects from Java classes like the following:

@XmlRootElement
public class Book {

    public String code;

    public HashMap<String, String> names;

}

This should be converted into JSON like this:

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

However I can not find a standard solution for this. Everybody seems to be implementing his own wrapper solution. This requirement seems extremly basic to me; I can't believe that this is the generally accepted solution to this, especially since Jersey is really one of the more fun parts of Java.

I've also tried upgrading to Jackson 1.8 which only gives me this, which is extremly obfusicated JSON:

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

Are there any proposed solutions for this?

samy-delux
  • 3,061
  • 2
  • 29
  • 32

4 Answers4

26

I don't know why this isn't the default setting, and it took me a while figuring it out, but if you want working JSON conversion with Jersey, add

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

to your web.xml and all your problems should be solved.

PS: you also need to get rid of the @XmlRootElement annotations to make it work

Jan
  • 1,445
  • 1
  • 16
  • 20
  • +1 for the tip. But be aware that the POJO Mapping will also return NULL values in the JSON. I prefer JAXB and work with Lists which include extra POJOs. – disco crazy Sep 13 '11 at 15:16
  • 4
    This is the server side setting. To do this on the client: ClientConfig clientConfig = new DefaultClientConfig(); clientConfig.getFeatures().put(JSONConfiguration.FEATURE_POJO_MAPPING, Boolean.TRUE); Client client = Client.create(clientConfig); – Jeremy Brooks Sep 28 '11 at 18:55
  • @Jan What if I need to convert _Book_ into json and into xml (request with _accepts_)? If I remove XmlRootElement xml serialization doesnt work anymore – Max Schmidt Nov 02 '11 at 16:19
  • 1
    Wow. This would have been a great answer except that adding as suggested to web.xml, no matter whether @XmlRootElement & related annotations are removed results in: com.sun.jersey.spi.container.ContainerRequest getEntity SEVERE: A message body reader for Java class com.hp.web.user.dto.ProfileDto, and Java type class com.hp.web.user.dto.ProfileDto, and MIME media type application/json; charset=UTF-8 was not found. – Russ Bateman Jul 05 '12 at 22:57
  • Yeah, what Russ said. This non-solution gives a "message body writer not found" error – ChrisO May 23 '14 at 17:36
  • I believe my solution above from 2012 was for Jersey 1.x – maybe the answers in this thread can help with Jersey 2.x https://stackoverflow.com/questions/5161466/how-do-i-use-the-jersey-json-pojo-support – Jan Aug 10 '18 at 14:16
7

You can use google-gson. Here is a sample code:

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

The output is {"code":"1234","names":{"Some":"Name","Manish":"Pandit"}}

lobster1234
  • 7,679
  • 26
  • 30
  • 1
    Thanks, but I was specifically interested in a solution integrated with Jersey. – samy-delux Apr 26 '11 at 20:20
  • I prefer this approach for returning complex data structure other than simple POJO. It works perfectly, it integrates with Jersey (you only have to return a "String" in your get method), and you don't have to mess up with web.xml of converters. – emas Jul 24 '14 at 15:17
4

I know it's been asked long time ago, but things changed mean time, so for the latest Jersey v2.22 that do not have anymore the package com.sun.jersey, these two dependencies added in the project pom.xml solved the same problem:

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

No need to add anything in web.xml. First dependency will instruct jersey to use jackson for POJO to JSON transformations. Second dependency will register jackson as jersey JSON provider.

Also for the null problem in POJO, add this annotation to the POJO class:

@JsonInclude(JsonInclude.Include.NON_NULL)
Lotzy
  • 489
  • 1
  • 6
  • 14
-2
@POST
@Consumes("application/json")
public void createBook(Book book)
{
 .....
 .....
}

Of course you need to have getter/setter for every property in Book.

Also the reason as why it is recommended to use wrapper class (which is usually a map) is to avoid creating multiple DTOs for every kind of data you want to send. It is easier to just serialize/deserialize using a map and as part of business logic convert it to a corresponding POJO for internal processing particularly if you are using that POJO for object relational mapping.

Prasanna
  • 3,703
  • 9
  • 46
  • 74