URI extensions in Jersey (.xml, .json) - java

I am trying to make a generic REST api using Jersey and I followed the following blog for this:
https://theza.ch/2009/08/11/uri-extensions-in-jersey/
So what is happening is that the server is working fine when I use .xml in my url and when I use .json, it gives a 500 Internal Server error. I have tried different things,but to no avail. Could anyone by any change know why this is happening in json and not for xml and how to fix this?
My code looks something like this:
#GET
#Path("/order/{product-key}/getorderid")
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getOrderIdByDomain(#Context HttpServletRequest request,
#PathParam("product-key") final String productKey,
#QueryParam("domain-name") final String domainName ) throws Exception
{
try
{
Integer response = doSomething();
return Response.status(200).entity(response).build();
}
catch (Exception lbe)
{
Hashtable response = new Hashtable();
response.put("Error",lbe.getMessage());
return Response.status(400).entity(response).build();
}
}
UPDATE:
After adding the jersey-json dependency, the 500 error changed to 200 OK but I am still getting an empty response. For xml, I am getting the expected response. Did someone face a similar issue? Please suggest something because I have tried a few things from other answers but it doesn't seem to be working.
StackTrace:
Caused by: java.lang.AbstractMethodError
at org.codehaus.jackson.map.AnnotationIntrospector$Pair.findSerializer(AnnotationIntrospector.java:1148)
at org.codehaus.jackson.map.ser.BasicSerializerFactory.findSerializerFromAnnotation(BasicSerializerFactory.java:362)
at org.codehaus.jackson.map.ser.BeanSerializerFactory.createSerializer(BeanSerializerFactory.java:252)
at org.codehaus.jackson.map.ser.StdSerializerProvider._createUntypedSerializer(StdSerializerProvider.java:782)
at org.codehaus.jackson.map.ser.StdSerializerProvider._createAndCacheUntypedSerializer(StdSerializerProvider.java:735)
at org.codehaus.jackson.map.ser.StdSerializerProvider.findValueSerializer(StdSerializerProvider.java:344)
at org.codehaus.jackson.map.ser.StdSerializerProvider.findTypedValueSerializer(StdSerializerProvider.java:420)
at org.codehaus.jackson.map.ser.StdSerializerProvider._serializeValue(StdSerializerProvider.java:601)
at org.codehaus.jackson.map.ser.StdSerializerProvider.serializeValue(StdSerializerProvider.java:256)
at org.codehaus.jackson.map.ObjectMapper.writeValue(ObjectMapper.java:1606)
at org.codehaus.jackson.jaxrs.JacksonJsonProvider.writeTo(JacksonJsonProvider.java:520)
at com.sun.jersey.json.impl.provider.entity.JacksonProviderProxy.writeTo(JacksonProviderProxy.java:160)
at com.sun.jersey.spi.container.ContainerResponse.write(ContainerResponse.java:306)
at com.sun.jersey.server.impl.application.WebApplicationImpl._handleRequest(WebApplicationImpl.java:1437)
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1349)
at com.sun.jersey.server.impl.application.WebApplicationImpl.handleRequest(WebApplicationImpl.java:1339)
at com.sun.jersey.spi.container.servlet.WebComponent.service(WebComponent.java:416)
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:537)
at com.sun.jersey.spi.container.servlet.ServletContainer.service(ServletContainer.java:699)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:727)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:303)
... 48 more
I am using the following dependencies: jersey-server, jersey-json version 1.8.

After trying for long to fix this, i switched to genson,
<dependency>
<groupId>com.owlike</groupId>
<artifactId>genson</artifactId>
<version>1.3</version>
</dependency>
It worked very easily. Will try to find later why Jackson didn't work.

After adding jar dependency as suggested in comment,
you need have additional parameter to decide xml or json named 'format' ,so now you can do like this by changing return statement as below
#GET
#Path("/order/{product-key}/{format}/getorderid")
#Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response getOrderIdByDomain(#Context HttpServletRequest request,
#PathParam("product-key") final String productKey,#PathParam("format") final String format,
#QueryParam("domain-name") final String domainName ) throws Exception
{
try
{
Integer response = doSomething();
return Response
// Set the status and Put your entity here.
.ok(entity)
// Add the Content-Type header to tell Jersey which format it should marshall the entity into.
.header(HttpHeaders.CONTENT_TYPE, "json".equals(format) ? MediaType.APPLICATION_JSON : MediaType.APPLICATION_XML)
.build();
}
catch (Exception lbe)
{
Hashtable response = new Hashtable();
response.put("Error",lbe.getMessage());
return Response.status(400).entity(response).build();
}
}

Related

Jersey MultiPartRequest receiving null

I am trying to build a multipart request from a client using MultipartEntitybuilder:
MultipartEntityBuilder builder = MultipartEntityBuilder.create();
builder.setMode(HttpMultipartMode.BROWSER_COMPATIBLE);
builder.addTextBody(MULTIPART_ID,
id,ContentType.TEXT_PLAIN);
builder.addTextBody(DOCT, doc,ContentType.TEXT_PLAIN);
final InputStreamBody fileBody = new InputStreamBody(cachedStream,
ContentType.create(MediaType.APPLICATION_OCTET_STREAM));
builder.addPart(CONTENT_PART, fileBody);
builder.addTextBody(OVERWRIT_PERMIT, String.valueOf(preventOverWrite),ContentType.TEXT_PLAIN);
final HttpPut putMethod= new HttpPut(url);
// putMethod.setHeader("content-type", "multipart/form-data"
putMethod.setEntity(builder.build());
final CloseableHttpResponse response = httpClient.execute(storeMethod);
The Resource Class reading this is as follows:
#Path("/somePath")
#PUT
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response uploadFunction(#FormDataParam(value="id") String id, #FormDataParam(value="doc") String doc,#FormDataParam(value="content") InputStream content,
#FormDataParam(value="overwrite") Boolean flag) throws SomeException {
I am using spring boot with Jersey2 . At this step when i try to read any of these I am getting null values for all these parameters. and hence the response gets an internal server error.
I have also added :
register(MultiPartFeature.class);
to both the client and server application.
Additionally Also added the dependency
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<scope>compile</scope>
</dependency>
Could someone please point out what am i missing ?Do i need to create an object instead of using #FormDataParam for the server? I have searched a lot over correctly using multipart, but I cannot fix it yet.
I fixed this after using a Decoder. Also needed a DTO of the representative object.So the above resource class was updated to
#Path("/somePath")
#PUT
#Consumes(MediaType.MULTIPART_FORM_DATA)
#Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response uploadFunction(FormDataMultiPart input){
Map<String, List<FormDataBodyPart>> bodyParts = input.getFields();
bodyParts.get("id").stream().findFirst().ifPresent(id-> dtoObject.setId(id.getValue()));
}
Similarly for other fields you can retrieve the value as well.

Exclude methods of jax-rs service from swagger.json

I´m using swagger on my java EE7 application (Glassfish as application server). Everything works fine except for a method with FormDataParam, which gave me the tradicional error:
org.glassfish.jersey.server.ContainerException: java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.AnnotationIntrospector.findPropertyIndex(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Integer;
I tried everything, but is just a method, so I do not want so badly this method in my swagger.json
How can I exclude this method from swagger. I tried:
#ApiModelProperty(hidden = true) and #ApiOperation(value="",hidden = true)
#POST
#Path("something")
#Produces(MediaType.TEXT_PLAIN)
#Consumes(MediaType.APPLICATION_FORM_URLENCODED)
public Response newsomething(#FormParam("something") String something,#Context HttpServletRequest request, #Context HttpServletResponse response) throws IOException {
return "something";
}
What I´m doing wrong?
It's related with Glassfish, it use different version of Jackson. You need to upgrade Glassfish/Jackson. More details:
java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.AnnotationIntrospector.findPropertyIndex(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Integer solution https://github.com/swagger-api/swagger-core/issues/1001
Upgrading Jackson in GlassFish 4.1
com.fasterxml.jackson.module.jaxb.JaxbAnnotationIntrospector not found --- while using Swagger
java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.AnnotationIntrospector.findPropertyIndex(Lcom/fasterxml/jackson/databind/introspect/Annotated;)Ljava/lang/Integer; solution - https://groups.google.com/forum/#!topic/swagger-swaggersocket/uQEn-anX_Og
java.lang.NoSuchMethodError: com.fasterxml.jackson.databind.AnnotationIntrospector.findPropertyDesc solution https://groups.google.com/forum/#!topic/swagger-swaggersocket/x-Pz_3orTCE
For me adding #ApiModelProperty(hidden = true) in paths worked
Is it wrong or correct?
#ApiModelProperty(hidden = true)
#GET
#Produces({MediaType.APPLICATION_JSON})
#ApiOperation(value = "return getApi ",
tags = {"getApi"},
notes = "Returns a Array of getApi",
hidden = true
)
#ApiResponses(value = {
#ApiResponse(response = GetApi.class, message = "", code = 200)
})
#Path("getApi")
public Response getApi(#Context HttpHeaders httpHeaders, #BeanParam QueryParamBean queryParamBean) {
// codes..
}

Java: Get JSON from POST with HttpServletRequest?

I'm trying to get the body of a POST request by using HttpServletRequest or UriInfo. Given a class like this one (reduced for this question):
#Path("/nodes")
#Produces({ MediaType.APPLICATION_JSON })
#Consumes({ MediaType.APPLICATION_JSON })
public class Nodes {
public NodeResource() {
//initial stuff goes here
}
/**
* gives an empty response. For testing only!
*/
#POST
#Consumes("application/json")
#Path("{id}/test-db-requests")
public Response giveNodes(#PathParam("id") final String id, #Context HttpServletRequest request, #Context UriInfo uriInfo){
//String readReq = request.getQueryString(); //would work for GET
MultivaluedMap<String,String> readParams = uriInfo.getQueryParameters();
LOG.debug("what is readParams?", readParams); //goes, but shows nothing
if (readParams != null) {
LOG.debug("null or not?"); //goes, too
for (Map.Entry<String,List<String>> entry: readParams.entrySet()) {
List<String> values = entry.getValue();
LOG.debug("params POST key: {}", entry.getKey()); // goes not
for (String val: values) {
LOG.debug("params POST values: {}", val);
}
LOG.debug("params POST next entry:::");
}
}
List<?> results = null; //currentDBRequest(id);
List<?> content = new ArrayList<>();
if (results != null) {
content = results;
}
return Response.ok(content).build();
}
}
Instead of using
MultivaluedMap<String,String> readParams = uriInfo.getQueryParameters();
//not possible at all - for GET only!? See first comment.
I also tried to use
Map<String,String[]> readParams = request.getParameterMap();
//what is about this one?
with different following code of course. But that did not work, either.
So when I fire a simple request like /nodes/546c9abc975a54c398167306/test-db-requests with the following body
{
"hi":"hello",
"green":"tree"
}
(using an JSON Array does not change anything)
and stuff in the HEADER (some informations):
Content-Type: application/json; charset=UTF-8
Accept: application/json, text/plain, */*
Connection: keep-alive
the result is disappointing, readParams is not null, but does not contain any data. Before I start to play with getReader I wanted to ask: what am I doing wrong? Is there a problem in my POST, in my Java code or in the used HttpServletRequest method(s)? Thanks!
Related questions (where I found some possible solutions), among others:
How can I grab all query parameters in Jersey JaxRS?
How to access parameters in a RESTful POST method
Alright, Jackson would actually do this for me. Just use the argument of the method, which you want to use. (See examples below.)
But you would probably not use a POST in combination with an id parameter. POST is usually used for saving fresh resources, which do not have an id (in the DB, a primary key). Moreover the path /api/{resource_name}/{id}/{some_view} would be useful for GET. Just api/{resource_name}/{id} for a GET (single entry) or a PUT (update an existing entry).
Assume you are in a resource for Pet.class. You want to catch the POSTs for this class in order to do something special with them, based on the view test-db-requests. Then do:
#POST
#Consumes("application/json")
#Path("{id}/test-db-requests")
public Response giveNodes(final String pet, #PathParam("id") final String id){
//do stuff for POST with a strigified JSON here
}
or
#POST
#Path("{id}/test-db-requests")
public Response giveNodes(final Pet pet, #PathParam("id") final String id){
//do stuff for POST with an instance of pet here (useful for non
//polymorphic resources
}

File upload along with other object in Jersey restful web service

I want to create an employee information in the system by uploading an image along with employee data. I am able to do it with different rest calls using jersey. But I want to achieve in one rest call.
I provide below the structure. Please help me how to do in this regard.
#POST
#Path("/upload2")
#Consumes({MediaType.MULTIPART_FORM_DATA,MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
public Response uploadFileWithData(
#FormDataParam("file") InputStream fileInputStream,
#FormDataParam("file") FormDataContentDisposition contentDispositionHeader,
Employee emp) {
//..... business login
}
Whenever I am trying to do, I get error in Chrome postman. The simple structure of my Employee json is given below.
{
"Name": "John",
"Age": 23,
"Email": "john#gmail.com",
"Adrs": {
"DoorNo": "12-A",
"Street": "Street-11",
"City": "Bangalore",
"Country": "Karnataka"
}
}
However I can do it by making two different call, but I want to achieve in one rest call so that I can receive the file as well as the actual data of the employee.
Request you to help in this regard.
You can't have two Content-Types (well technically that's what we're doing below, but they are separated with each part of the multipart, but the main type is multipart). That's basically what you are expecting with your method. You are expecting mutlipart and json together as the main media type. The Employee data needs to be part of the multipart. So you can add a #FormDataParam("emp") for the Employee.
#FormDataParam("emp") Employee emp) { ...
Here's the class I used for testing
#Path("/multipart")
public class MultipartResource {
#POST
#Path("/upload2")
#Consumes({MediaType.MULTIPART_FORM_DATA})
public Response uploadFileWithData(
#FormDataParam("file") InputStream fileInputStream,
#FormDataParam("file") FormDataContentDisposition cdh,
#FormDataParam("emp") Employee emp) throws Exception{
Image img = ImageIO.read(fileInputStream);
JOptionPane.showMessageDialog(null, new JLabel(new ImageIcon(img)));
System.out.println(cdh.getName());
System.out.println(emp);
return Response.ok("Cool Tools!").build();
}
}
First I just tested with the client API to make sure it works
#Test
public void testGetIt() throws Exception {
final Client client = ClientBuilder.newBuilder()
.register(MultiPartFeature.class)
.build();
WebTarget t = client.target(Main.BASE_URI).path("multipart").path("upload2");
FileDataBodyPart filePart = new FileDataBodyPart("file",
new File("stackoverflow.png"));
// UPDATE: just tested again, and the below code is not needed.
// It's redundant. Using the FileDataBodyPart already sets the
// Content-Disposition information
filePart.setContentDisposition(
FormDataContentDisposition.name("file")
.fileName("stackoverflow.png").build());
String empPartJson
= "{"
+ " \"id\": 1234,"
+ " \"name\": \"Peeskillet\""
+ "}";
MultiPart multipartEntity = new FormDataMultiPart()
.field("emp", empPartJson, MediaType.APPLICATION_JSON_TYPE)
.bodyPart(filePart);
Response response = t.request().post(
Entity.entity(multipartEntity, multipartEntity.getMediaType()));
System.out.println(response.getStatus());
System.out.println(response.readEntity(String.class));
response.close();
}
I just created a simple Employee class with an id and name field for testing. This works perfectly fine. It shows the image, prints the content disposition, and prints the Employee object.
I'm not too familiar with Postman, so I saved that testing for last :-)
It appears to work fine also, as you can see the response "Cool Tools". But if we look at the printed Employee data, we'll see that it's null. Which is weird because with the client API it worked fine.
If we look at the Preview window, we'll see the problem
There's no Content-Type header for the emp body part. You can see in the client API I explicitly set it
MultiPart multipartEntity = new FormDataMultiPart()
.field("emp", empPartJson, MediaType.APPLICATION_JSON_TYPE)
.bodyPart(filePart);
So I guess this is really only part of a full answer. Like I said, I am not familiar with Postman So I don't know how to set Content-Types for individual body parts. The image/png for the image was automatically set for me for the image part (I guess it was just determined by the file extension). If you can figure this out, then the problem should be solved. Please, if you find out how to do this, post it as an answer.
See UPDATE below for solution
And just for completeness...
See here for more about MultiPart with Jersey.
Basic configurations:
Dependency:
<dependency>
<groupId>org.glassfish.jersey.media</groupId>
<artifactId>jersey-media-multipart</artifactId>
<version>${jersey2.version}</version>
</dependency>
Client config:
final Client client = ClientBuilder.newBuilder()
.register(MultiPartFeature.class)
.build();
Server config:
// Create JAX-RS application.
final Application application = new ResourceConfig()
.packages("org.glassfish.jersey.examples.multipart")
.register(MultiPartFeature.class);
If you're having problems with the server configuration, one of the following posts might help
What exactly is the ResourceConfig class in Jersey 2?
152 MULTIPART_FORM_DATA: No injection source found for a parameter of type public javax.ws.rs.core.Response
UPDATE
So as you can see from the Postman client, some clients are unable to set individual parts' Content-Type, this includes the browser, in regards to it's default capabilities when using FormData (js).
We can't expect the client to find away around this, so what we can do, is when receiving the data, explicitly set the Content-Type before deserializing. For example
#POST
#Path("upload2")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response uploadFileAndJSON(#FormDataParam("emp") FormDataBodyPart jsonPart,
#FormDataParam("file") FormDataBodyPart bodyPart) {
jsonPart.setMediaType(MediaType.APPLICATION_JSON_TYPE);
Employee emp = jsonPart.getValueAs(Employee.class);
}
It's a little extra work to get the POJO, but it is a better solution than forcing the client to try and find it's own solution.
Another option is to use a String parameter and use whatever JSON library you use to deserialze the String to the POJO (like Jackson ObjectMapper). With the previous option, we just let Jersey handle the deserialization, and it will use the same JSON library it uses for all the other JSON endpoints (which might be preferred).
Asides
There is a conversation in these comments that you may be interested in if you are using a different Connector than the default HttpUrlConnection.
You can access the Image File and data from a form using MULTIPART FORM DATA By using the below code.
#POST
#Path("/UpdateProfile")
#Consumes(value={MediaType.APPLICATION_JSON,MediaType.MULTIPART_FORM_DATA})
#Produces(value={MediaType.APPLICATION_JSON,MediaType.APPLICATION_XML})
public Response updateProfile(
#FormDataParam("file") InputStream fileInputStream,
#FormDataParam("file") FormDataContentDisposition contentDispositionHeader,
#FormDataParam("ProfileInfo") String ProfileInfo,
#FormDataParam("registrationId") String registrationId) {
String filePath= "/filepath/"+contentDispositionHeader.getFileName();
OutputStream outputStream = null;
try {
int read = 0;
byte[] bytes = new byte[1024];
outputStream = new FileOutputStream(new File(filePath));
while ((read = fileInputStream.read(bytes)) != -1) {
outputStream.write(bytes, 0, read);
}
outputStream.flush();
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (outputStream != null) {
try {
outputStream.close();
} catch(Exception ex) {}
}
}
}
When I tried #PaulSamsotha's solution with Jersey client 2.21.1, there was 400 error. It worked when I added following in my client code:
MediaType contentType = MediaType.MULTIPART_FORM_DATA_TYPE;
contentType = Boundary.addBoundary(contentType);
Response response = t.request()
.post(Entity.entity(multipartEntity, contentType));
instead of hardcoded MediaType.MULTIPART_FORM_DATA in POST request call.
The reason this is needed is because when you use a different Connector (like Apache) for the Jersey Client, it is unable to alter outbound headers, which is required to add a boundary to the Content-Type. This limitation is explained in the Jersey Client docs. So if you want to use a different Connector, then you need to manually create the boundary.
Your ApplicationConfig should register the MultiPartFeature.class from the glassfish.jersey.media.. so as to enable file upload
#javax.ws.rs.ApplicationPath(ResourcePath.API_ROOT)
public class ApplicationConfig extends ResourceConfig {
public ApplicationConfig() {
//register the necessary headers files needed from client
register(CORSConfigurationFilter.class);
//The jackson feature and provider is used for object serialization
//between client and server objects in to a json
register(JacksonFeature.class);
register(JacksonProvider.class);
//Glassfish multipart file uploader feature
register(MultiPartFeature.class);
//inject and registered all resources class using the package
//not to be tempered with
packages("com.flexisaf.safhrms.client.resources");
register(RESTRequestFilter.class);
}
I used file upload example from,
http://www.mkyong.com/webservices/jax-rs/file-upload-example-in-jersey/
in my resource class i have below method
#POST
#Path("/upload")
#Consumes(MediaType.MULTIPART_FORM_DATA)
public Response attachupload(#FormDataParam("file") byte[] is,
#FormDataParam("file") FormDataContentDisposition fileDetail,
#FormDataParam("fileName") String flename){
attachService.saveAttachment(flename,is);
}
in my attachService.java i have below method
public void saveAttachment(String flename, byte[] is) {
// TODO Auto-generated method stub
attachmentDao.saveAttachment(flename,is);
}
in Dao i have
attach.setData(is);
attach.setFileName(flename);
in my HBM mapping is like
<property name="data" type="binary" >
<column name="data" />
</property>
This working for all type of files like .PDF,.TXT, .PNG etc.,
The request type is multipart/form-data and what you are sending is essentially form fields that go out as bytes with content boundaries separating different form fields.To send an object representation as form field (string), you can send a serialized form from the client that you can then deserialize on the server.
After all no programming environment object is actually ever traveling on the wire. The programming environment on both side are just doing automatic serialization and deserialization that you can also do. That is the cleanest and programming environment quirks free way to do it.
As an example, here is a javascript client posting to a Jersey example service,
submitFile(){
let data = new FormData();
let account = {
"name": "test account",
"location": "Bangalore"
}
data.append('file', this.file);
data.append("accountKey", "44c85e59-afed-4fb2-884d-b3d85b051c44");
data.append("device", "test001");
data.append("account", JSON.stringify(account));
let url = "http://localhost:9090/sensordb/test/file/multipart/upload";
let config = {
headers: {
'Content-Type': 'multipart/form-data'
}
}
axios.post(url, data, config).then(function(data){
console.log('SUCCESS!!');
console.log(data.data);
}).catch(function(){
console.log('FAILURE!!');
});
},
Here the client is sending a file, 2 form fields (strings) and an account object that has been stringified for transport. here is how the form fields look on the wire,
On the server, you can just deserialize the form fields the way you see fit. To finish this trivial example,
#POST
#Path("/file/multipart/upload")
#Consumes({MediaType.MULTIPART_FORM_DATA})
public Response uploadMultiPart(#Context ContainerRequestContext requestContext,
#FormDataParam("file") InputStream fileInputStream,
#FormDataParam("file") FormDataContentDisposition cdh,
#FormDataParam("accountKey") String accountKey,
#FormDataParam("account") String json) {
System.out.println(cdh.getFileName());
System.out.println(cdh.getName());
System.out.println(accountKey);
try {
Account account = Account.deserialize(json);
System.out.println(account.getLocation());
System.out.println(account.getName());
} catch (Exception e) {
e.printStackTrace();
}
return Response.ok().build();
}

No content to map due to end-of-input jackson parser

I am getting this response from the server {"status":"true","msg":"success"}
I am trying to parse this json string using Jackson parser library but somehow I am facing mapping-exception stating
com.fasterxml.jackson.databind.JsonMappingException: No content to map due to end-of-input
at [Source: java.io.StringReader#421ea4c0; line: 1, column: 1]
Why do we get this kind of exceptions?
How to understand what is causing this exception?
I am trying to parse using following way:
StatusResponses loginValidator = null;
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(Feature.AUTO_CLOSE_SOURCE, true);
try {
String res = result.getResponseAsString();//{"status":"true","msg":"success"}
loginValidator = objectMapper.readValue(result.getResponseAsString(), StatusResponses.class);
} catch (Exception e) {
e.printStackTrace();
}
StatusResponse class
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonPropertyOrder({ "status","msg" })
public class StatusResponses {
#JsonProperty("status")
public String getStatus() {
return status;
}
#JsonProperty("status")
public void setStatus(String status) {
this.status = status;
}
#JsonProperty("msg")
public String getMessage() {
return message;
}
#JsonProperty("msg")
public void setMessage(String message) {
this.message = message;
}
#JsonProperty("status")
private String status;
#JsonProperty("msg")
private String message;
private Map<String, Object> additionalProperties = new HashMap<String, Object>();
#JsonGetter
public Map<String, Object> getAdditionalProperties() {
return additionalProperties;
}
#JsonSetter
public void setAdditionalProperties(Map<String, Object> additionalProperties) {
this.additionalProperties = additionalProperties;
}
}
import com.fasterxml.jackson.core.JsonParser.Feature;
import com.fasterxml.jackson.databind.ObjectMapper;
StatusResponses loginValidator = null;
ObjectMapper objectMapper = new ObjectMapper();
objectMapper.configure(Feature.AUTO_CLOSE_SOURCE, true);
try {
String res = result.getResponseAsString();//{"status":"true","msg":"success"}
loginValidator = objectMapper.readValue(res, StatusResponses.class);//replaced result.getResponseAsString() with res
} catch (Exception e) {
e.printStackTrace();
}
Don't know how it worked and why it worked? :( but it worked
In my case the problem was caused by my passing a null InputStream to the ObjectMapper.readValue call:
ObjectMapper objectMapper = ...
InputStream is = null; // The code here was returning null.
Foo foo = objectMapper.readValue(is, Foo.class)
I am guessing that this is the most common reason for this exception.
I could fix this error. In my case, the problem was at client side. By mistake I did not close the stream that I was writing to server. I closed stream and it worked fine. Even the error sounds like server was not able to identify the end-of-input.
OutputStream out = new BufferedOutputStream(urlConnection.getOutputStream());
out.write(jsonstring.getBytes());
out.close() ; //This is what I did
The problem for me was that I read the response twice as follows:
System.out.println(response.body().string());
getSucherResponse = objectMapper.readValue(response.body().string(), GetSucherResponse.class);
However, the response can only be read once as it is a stream.
I had a similar error today and the issue was the content-type header of the post request. Make sure the content type is what you expect. In my case a multipart/form-data content-type header was being sent to the API instead of application/json.
This error is sometimes (often?) hiding the real problem: a failure condition could be causing the content to be incorrect, which then fails to deserialize.
In my case, today, I was making HTTP calls and (foolishly) omitted to check the HTTP status code before trying to unmarshal the body of the response => my real problem was actualy that I had some authentication error, which caused a 401 Unauthorized to be sent back to me, with an empty body. Since I was unmarshalling that empty body directly without checking anything, I was getting this No content to map due to end-of-input, without getting any clue about the authentication issue.
I got this error when sending a GET request with postman.
The request required no parameters.
My mistake was I had a blank line in the request body.
In my case I was reading the stream in a jersey RequestEventListener I created on the server side to log the request body prior to the request being processed. I then realized that this probably resulted in the subsequent read to yield no string (which is what is passed over when the business logic is run). I verified that to be the case.
So if you are using streams to read the JSON string be careful of that.
A simple fix could be Content-Type: application/json
You are probably making a REST API call to get the response.
Mostly you are not setting Content-Type: application/json when you the request.
Content-Type: application/x-www-form-urlencoded will be chosen which might be causing this exception.
I know this is weird but when I changed GetMapping to PostMapping for both client and server side the error disappeared.
Both client and server are Spring boot projects.
Please Don't overthink this issue, simply handle the null pointer exception here as your response is received null and you will get your solution.
In my case I had race condition between 2 threads trying to write and read the file simultaneously. I added the "synchronized" keyword on my methods which writes and reads to the file and the error is gone.
For one, #JsonProperty("status") and #JsonProperty("msg") should only be there only when declaring the fields, not on the setters and geters.
In fact, the simplest way to parse this would be
#JsonAutoDetect //if you don't want to have getters and setters for each JsonProperty
public class StatusResponses {
#JsonProperty("status")
private String status;
#JsonProperty("msg")
private String message;
}

Categories