Unable to find a MessageBodyReader of content-type application/json and type class java.lang.String - json

I am using RestEasy client with jackson providers and getting the above error
clientside code is:
ClientRequest request = new ClientRequest(url);
request.accept(MediaType.APPLICATION_JSON);
ClientResponse<String> response = request.get(String.class);
if (response.getStatus() != 200) {
throw new RuntimeException("Failed : HTTP error code : " + response.getStatus());
}
BufferedReader br =
new BufferedReader(new InputStreamReader(new ByteArrayInputStream(response.getEntity().getBytes())));
response.getEntity() is throwing ClientResponseFailure exception with the error being
Unable to find a MessageBodyReader of content-type application/json and type class java.lang.String
My server side code is below:
#GET
#Path("/{itemId}")
#Produces(MediaType.APPLICATION_JSON)
public String item(#PathParam("itemId") String itemId) {
//custom code
return gson.toJSON(object);
}

You could try to add the following dependency to your maven pom.
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson-provider</artifactId>
<version>2.3.4.Final</version>
</dependency>

The problem actually is that RestEasy is unable to find the Jackson provider. I had to manually register it by the following code:
ResteasyProviderFactory instance=ResteasyProviderFactory.getInstance();
RegisterBuiltin.register(instance);
instance.registerProvider(ResteasyJacksonProvider.class);
Everything is working fine with this. But I am still unhappy with the solution as Resteasy is supposed to scan for the providers and register them automatically.

Client client = ClientBuilder.newBuilder().register(ResteasyJacksonProvider.class).build();

Just adding the line org.jboss.resteasy.plugins.providers.jackson.ResteasyJacksonProvider into META-INF/services/javax.ws.rs.ext.Providers file, solves the problem.
This file is included into resteasy-jackson-providers.jar but same file is also included into another jar, restasy-jaxrs.jar and for an executable jar file, that use both these jars, they are not merged !!

I had a similar problem and I realized that the problem was related with the version of resteasy-jackson-provider. I just moved from 3.0.4.Final to 3.0.5.Final and the problem disappeared.
Additionally I also realized that if I change the third line to the following the result was the expected with no need to change the dependencies.
Response response = request.get(Object.class).toString();

Things that had made work my code were that I added:
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-jackson2-provider</artifactId>
<version>&{resteasy.version}</version>
</dependency>
Beside this I don't know why but it seems that resteasy not initializing providers when client were created, this means that it required to init them manually:
ResteasyProviderFactory instance=ResteasyProviderFactory.getInstance();
RegisterBuiltin.register(instance);
instance.registerProvider(ResteasyJackson2Provider.class);
In general it's enough to run the client.

I don't know the full rationale behind that but we've hit the exact same problem (multiple times :P) and you need to change the MediaType to TEXT_PLAIN.
Or you can also let JAX-RS do the job for you: instead of doing gson.toJSON(object), simply return object and change your method signature to whatever class that is. JAX-RS (RESTEasy in your case) will automatically call Jackson (if it's properly configured) and serialize your object to json. Then on your client side you would request for that same class instead of String and everything should work on its own. I'm not particularly familiar with ClientRequest/Response so it might not work as I said; we use RESTEasy proxy functionality on the client side instead (see ProxyFactory). Nevertheless, JAX-RS/RESTEasy can do json serialize/deserialize automatically on the client side too so there's definetly a way.

If you really want to by-pass goodness of JAX-RS actually doing serialization for you (instead of manually calling GSON), use StreamingOutput (i.e. wrap outputter as StreamingOutput, return that object).

Related

Unexpected behaviour from Gson

I developed a small application that stores data coming from a device: I chose to store data in JSON format, and the serialization/deserialization of the data works just fine, even if it involves some custom types created by me...but only I work in the IDE (Eclipse, for that matter).
When I export a runnable JAR file though, the deserialization of the data encounters some kind of problem, because the software always throws this exception:
Caused by: java.lang.UnsupportedOperationException: Cannot allocate class LocalDateTime
at com.google.gson.internal.UnsafeAllocator$4.newInstance(UnsafeAllocator.java:104)
at com.google.gson.internal.ConstructorConstructor$14.construct(ConstructorConstructor.java:225)
... 88 common frames omitted
I thought I'd encounter problems with custom types, not a built-in one. At this point, I discovered two things:
if I use a full JRE 9 to run the JAR file, the exception is not thrown: I double checked the modules included in the custom JRE I created with Jlink.exe, and everything is included correctly. I still want to use a smaller JRE, so I did not investigate further yet (I guess this explains why in the IDE it works perfectly)
I added a custom deserializer to the Gson object (see below), with which I simply manually converted the JSON string into a valid data, and that avoided the exception on the LocalDateTime class...but the exception reappeared simply on another class, this time a custom-made one.
At this point, I guess I can simply add a deserializer for each data type that causes problem, but I'm wondering why the issue won't happen with a full JRE, and why a smaller JRE causes this, even if all the modules required are included. Maybe it's worth mentioning also that I added no custom serializer to the Gson object that saves the data, it is all serialized as per Gson default.
LocalDateTime deserializer:
#Override
public LocalDateTime deserialize(JsonElement json, java.lang.reflect.Type type,
JsonDeserializationContext jsonDeserializationContext) throws JsonParseException {
JsonObject joDate = json.getAsJsonObject().get("date").getAsJsonObject();
JsonObject joTime = json.getAsJsonObject().get("time").getAsJsonObject();
//JSON example: {"date":{"year":2019,"month":1,"day":9},"time":{"hour":6,"minute":14,"second":1,"nano":0}
return LocalDateTime.of(joDate.get("year").getAsInt(),
joDate.get("month").getAsInt(),
joDate.get("day").getAsInt(),
joTime.get("hour").getAsInt(),
joTime.get("minute").getAsInt(),
joTime.get("second").getAsInt(),
joTime.get("nano").getAsInt());
}
}
Jdeps.deps modules list:
com.google.gson
java.base
javafx.base
javafx.controls
javafx.fxml
javafx.graphics
org.slf4j
After the answer I received, I opened an issue here.
TL;DR
You need a runtime image (e.g. full JDK or something built with jlink) that includes the module jdk.unsupported.
Full Answer
GSON wants to create instances of classes it deserializes without calling any constructors (so nothing gets initialized without GSON saying so). This can't normally be done, but sun.misc.Unsafe offers a way to do this with the method allocateInstance. To that end, GSON needs an instance of sun.misc.Unsafe. The topmost frame in the call stack is from UnsafeAllocator, which uses common trickery to get Unsafe.
The problem is, sun.misc.Unsafe is in module jdk.unsupported, which is present in a full JDK but you won't usually find in runtime images.
When creating your runtime image with jlink, make sure to include the option --add-modules jdk.unsupported and you should be good to go.
Arguably, GSON should declare an optional dependency on jdk.unsupported with requires static.
I have faced the same issue when packing compose a desktop application.
update build.gradle file, add an unsupported module.
compose.desktop {
application {
mainClass = "MainKt"
nativeDistributions {
targetFormats(TargetFormat.Dmg, TargetFormat.Msi, TargetFormat.Deb)
packageName = "admin"
packageVersion = "1.0.0"
modules("java.sql")
modules("jdk.unsupported")
}
}
}

MessageBodyWriter not found for media type=application/json when returning JSON in REST web service with Jersey

I am trying to create a very simple REST service using Jersey. Here is the service code
#Path("/UserService")
public class UserService {
#GET
#Path("/users")
#Produces(MediaType.APPLICATION_XML)
public List<User> getUsers() {
User user = new User(1, "Thomas", "Greene");
List<User> userList = new ArrayList<User>();
userList.add(user);
return userList;
}
}
When I run it through Postman, it returns me a XML response
Now, I want to get a JSON response back. So, I changed the mediatype to application/json:
#Path("/UserService")
public class UserService {
#GET
#Path("/users")
#Produces(MediaType.APPLICATION_JSON)
public List<User> getUsers(){
User user = new User(1, "Thomas", "Greene");
List<User> userList = new ArrayList<User>();
userList.add(user);
return userList;
}
}
It gives me the below error in Tomcat logs:
SEVERE: MessageBodyWriter not found for media type=application/json, type=class java.util.ArrayList, genericType=java.util.List.
Can someone please guide me how to get a JSON response back?
To use Jackson 2.x as your JSON provider you need to add jersey-media-json-jackson module to your pom.xml file:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-json-jackson</artifactId>
<version>2.22.2</version>
</dependency>
And then register the JacksonFeature in your Application/ResourceConfig subclass.
For more details, have a look at Jersey documentation.
I am a bit upset about JAXB binding as well at the moment, therefore let me summarize my findings here - please correct me if I say something stupid:
Of course you have to have a library to do the JSON (de)serialization, in my case it is Moxy.
You have to tell JAXB which classes it should support. There are multiple ways to do that, the simplest seems to be to add a jaxb.properties file in the directory matching your classes and its sole content is the text javax.xml.bind.context.factory=org.eclipse.persistence.jaxb.JAXBContextFactory. With the directory I mean, if your classes are in the directory src/main/java and there the package com.pkg1.entities, add this file in src/main/resources and there as com/pkg1/entities/jaxb.properties.
By default JAXB works on POJOs. So you need to have a constructor without arguments, a get- and a set-method. Only then this field will be present in the JSON.
What I do often is to add a second constructor that gets the runtime object passed in and sets all fields to be exposed directly. Hence I do not need and do not want a set-method. Solution is to annotate the get method with #XmlElement.
Did I say you need an empty/default constructor? Took me once three hours to find out why class1 was working fine, class2 got the MessageBodyWriter error. I had forgotten the constructor. Grrrrr.
You get the same error (I believe) when the class is configured fine but one of its fields returns a type it cannot serialize.
I believe to have had one case where the class annotation #XmlRootElement caused that error. Not sure but I barely use that annotation at the moment.
In case you have a List as one of the elements to be turned into a Json array, JAXB will use the myAbstract class to do the serialization. Not very useful, you want the actual objects to be serialized. But how should JAXB know who implemented/extended this class? You have to tell with the annotation #XmlSeeAlso. So the MyAbstract class gets a class annotation #XmlSeeAlso({MyConcrete1.class, MyConcrete2.class}). At least Moxy does add then an additional type field telling the consumer which class it was. Makes total sense.
Although you can return userList the better option would be to return Response.ok().entity(userList).build(); Then you can return errors as well. Logically it is the same.
Watchout what kind of data types you use. String is fine, ArrayList as well, Hashtable not. Depends on the serializer you use as well.
I hope this helps others.
You need a json serializer on your class path to make this work.
Just add jackson and jersey will use this in the writer.
E.g. if you are using maven, add this to the pom.xml
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.7.4</version>
</dependency>
Your xml was working so I assume that you have #XmlRootElement annotation in your User class.
The thing is, it knows how to convert it to xml with the annotation #XmlRootElement but it doesn't know how to convert it to JSON.
So for making it convert everything to JSON with the same annotation of xml(ie #XmlRootElement) we can add
jersey-media-moxy-<whatever version>.jar
or for maven users
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-moxy</artifactId>
</dependency>
Also it should have a no argument constructor
I tried a ton of these dependencies but none of them worked for my version of Jersey3. What I needed to do was turn the Arraylist into an actual array. I managed this with toArray() and it started serializing correctly!

Cannot Produce JSON with Maven quickstart grizzly Archetype

I've put some good hours trying to get my grizzly webserver to produce JSON.
Generated from maven archetype using Intellij, details:
groupId untitled3
artifactId untitled3
version 1.0-SNAPSHOT
archetypeGroupId org.glassfish.jersey.archetypes
archetypeArtifactId jersey-quickstart-grizzly2
archetypeVersion 2.0-m05
All online examples enable JSON using
rc.put(JSONConfiguration.FEATURE_POJO_MAPPING, true);
However, this does not work in jersey 2.x, put method does not exist.
In the application Main class, there is instructions on uncommenting a line of code to get JSON to work. When i uncomment this line of code, the method used does not exist.
public static HttpServer startServer() {
// create a resource config that scans for JAX-RS resources and providers
// in untitled2 package
final ResourceConfig rc = new ResourceConfig().packages("untitled2");
// uncomment the following line if you want to enable
// support for JSON on the service (you also have to uncomment
// dependency on jersey-media-json module in pom.xml)
// --
rc.addModule(org.glassfish.jersey.media.json.JsonJaxbModule);
// create and start a new instance of grizzly http server
// exposing the Jersey application at BASE_URI
return GrizzlyHttpServerFactory.createHttpServer(URI.create(BASE_URI), rc);
}
When i try to serve a JSON response from a POJO object i get this error:
org.glassfish.jersey.message.internal.MessageBodyProviderNotFoundException: MessageBodyWriter not found for media type=application/json, type=class com.example.JsonPost, genericType=class com.example.JsonPost.
I've no idea where to begin to look really. I've googled, plown through documentation and looked through user groups...
Make sure you uncomment the dependency in the POM. Then change the addModule call to this:
rc.addModules(new org.glassfish.jersey.media.json.JsonJaxbModule());
and be sure to include the correct #Produces annotation on your resource:
#Produces(MediaType.APPLICATION_JSON)

How to set the read and write providers for JSON using JAX-RS in Apache Tomee plus

I can return simple text from my Pojo but it fails when I changed to return JSON. I have the correct annotations for produces and consumes in my methods and I checked that the content negotation between Jquery and the server is correct.
It seems to be that the conversion to JSON just fails. I copied the Jackson jars in webapps/tomee/lib. I also tried the lib folder of my application.
The error message when doing a GET:
No message body writer has been found for response class <myClass>
This error is fired by the JAXRSOut Intercepter class
The error when doing a Post is:
No message body reader has been found for request class <myClass>
This error is fired by the JAXRSUtils class
Thanks
I made this work by listing the JAX-RS providers in conf/system.properties file of TomEE. Then I added the jars to TomEE's lib folder. The documentation in the website states that one should drop the jars on webapp/tomee/lib, but that did not work for me.
conf/system.properties
openejb.cxf.jax-rs.providers = org.codehaus.jackson.jaxrs.JacksonJsonProvider, org.codehaus.jackson.jaxrs.JacksonJaxbJsonProvider
Jars I dropped:
jackson-mapper-asl-1.9.9.jar
jackson-core-asl-1.9.9.jar
jackson-jaxrs-1.9.9
If you use jersey you could try Genson library http://code.google.com/p/genson/.
Latest version 0.92 provides automatic detection of json support in jax-rs web services with jersey.
You should only have to drop the jar and voila! :)

Getting Jersey's JSON POJO support to work from embedded Jetty?

I'm trying to get a simple object to JSON format from my Jetty server using Jersey.
I keep getting the error:
SEVERE: A message body writer for Java class my.package.resources.Album, and Java type class my.package.resources.Album, and MIME media type application/json was not found
One problem is that I'm not sure where to put the POJOMappingFeature with Jetty, I have tried the following but neither changes the error:
myServletHolder.setInitParameter("com.sun.jersey.api.json.POJOMappingFeature", "true");
and
myServletContextHandler.setInitParameter("com.sun.jersey.api.json.POJOMappingFeature", "true");
I know the basic REST functionality of Jersey is working because before I tried the JSON output I used plain text and it worked great.
I was super disappointed when I saw this one not answered as I've been fighting it all day. Finally figured it out so here's what worked for me.
What I was doing:
ServletHolder servletHolder = new ServletHolder(new ServletContainer(new PackagesResourceConfig("my.package.with.resources")));
servletHolder.setInitParameter("com.sun.jersey.api.json.POJOMappingFeature", "true");
What I needed to do:
Map<String,Object> initMap = new HashMap<String, Object>();
initMap.put("com.sun.jersey.api.json.POJOMappingFeature", "true");
initMap.put("com.sun.jersey.config.property.packages", "my.package.with.resources");
ServletHolder servletHolder = new ServletHolder(new ServletContainer(new PackagesResourceConfig(initMap)));
I had to make sure to specify my Jersey packages and my POJOMappingFeature flag as init params. Hope this helps.
Ensure that you have jackson json-parser and JAXB lib in your classpath.