Trying to use Spring Boot REST to Read JSON String from POST

JavaJsonRestSpring MvcSpring Boot

Java Problem Overview


Am using the latest version of Spring Boot to read in a sample JSON via Restful Web Service...

Here's my pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"       
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 
         http://maven.apache.org/maven-v4_0_0.xsd"
	     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

	<modelVersion>4.0.0</modelVersion>

	<groupId>org.springframework</groupId>
	<artifactId>myservice</artifactId>
	<version>0.1.0</version>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>1.2.2.RELEASE</version>
	</parent>

	<properties>
		<java.version>1.7</java.version>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
	</properties>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<dependency>
			<groupId>org.springframework.data</groupId>
			<artifactId>spring-data-rest-webmvc</artifactId>
		</dependency>
		<dependency>
	</dependencies>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>

	<repositories>
		<repository>
			<id>spring-releases</id>
			<name>Spring Releases</name>
			<url>https://repo.spring.io/libs-release</url>
		</repository>
		<repository>
			<id>org.jboss.repository.releases</id>
			<name>JBoss Maven Release Repository</name>
			<url>https://repository.jboss.org/nexus/content/repositories/releases</url>
		</repository>
	</repositories>

	<pluginRepositories>
		<pluginRepository>
			<id>spring-releases</id>
			<name>Spring Releases</name>
			<url>https://repo.spring.io/libs-release</url>
		</pluginRepository>
	</pluginRepositories>

</project>

Here's my web service code:

import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/myservice")
public class BaseService {	
	
	@RequestMapping(value="/process", method = RequestMethod.POST)
	public void process(@RequestBody String payload) throws Exception {
		System.out.println(payload);
	}
}

When I invoke it using the following command:

curl -H "Accept: application/json" -H "Content-type: application/json" 
-X POST -d '{"name":"value"}' http://localhost:8080/myservice/process

I get this error message:

{"timestamp":1427515733546,"status":400,
 "error":"Bad Request",

"exception":
"org.springframework.http.converter.HttpMessageNotReadableException","
 message":
 "Could not read JSON: Can not deserialize instance of java.lang.String
  out of START_OBJECT token\n at 

 [Source: java.io.PushbackInputStream@8252f; line: 1, column: 1]; 
  nested    exception is com.fasterxml.jackson.databind.JsonMappingException:
  Can not deserialize instance of java.lang.String out of START_OBJECT token\n    
  at [Source: java.io.PushbackInputStream@8252f; line: 1, column: 1]",
  "path":"/myservice/process"

The only thing I am trying to do is pass in some valid JSON (as a string via curl) and to see if the String payload enters the process method as {"name":"value"}

What am I possibly doing wrong?

Thank you for taking the time to read this...

Java Solutions


Solution 1 - Java

I think the simplest/handy way to consuming JSON is using a Java class that resembles your JSON: https://stackoverflow.com/a/6019761

But if you can't use a Java class you can use one of these two solutions.

Solution 1: you can do it receiving a Map<String, Object> from your controller:

@RequestMapping(
    value = "/process", 
    method = RequestMethod.POST)
public void process(@RequestBody Map<String, Object> payload) 
    throws Exception {
  
  System.out.println(payload);
  
}

Using your request:

curl -H "Accept: application/json" -H "Content-type: application/json" \
-X POST -d '{"name":"value"}' http://localhost:8080/myservice/process

Solution 2: otherwise you can get the POST payload as a String:

@RequestMapping(
    value = "/process", 
    method = RequestMethod.POST,
    consumes = "text/plain")
public void process(@RequestBody String payload) throws Exception {
  
  System.out.println(payload);
  
}

Then parse the string as you want. Note that must be specified consumes = "text/plain" on your controller. In this case you must change your request with Content-type: text/plain:

curl -H "Accept: application/json" -H "Content-type: text/plain" -X POST \
-d '{"name":"value"}' http://localhost:8080/myservice/process

Solution 2 - Java

To receive arbitrary Json in Spring-Boot, you can simply use Jackson's JsonNode. The appropriate converter is automatically configured.

    @PostMapping(value="/process")
    public void process(@RequestBody com.fasterxml.jackson.databind.JsonNode payload) {
        System.out.println(payload);
    }

Solution 3 - Java

To add on to Andrea's solution, if you are passing an array of JSONs for instance

[    {"name":"value"},    {"name":"value2"}]

Then you will need to set up the Spring Boot Controller like so:

@RequestMapping(
    value = "/process", 
    method = RequestMethod.POST)
public void process(@RequestBody Map<String, Object>[] payload) 
    throws Exception {

    System.out.println(payload);

}

Solution 4 - Java

To further work with array of maps, the followings could help:

@RequestMapping(value = "/process", method = RequestMethod.POST, headers = "Accept=application/json")
public void setLead(@RequestBody Collection<? extends Map<String, Object>> payload) throws Exception {
  	
  List<Map<String,Object>> maps = new ArrayList<Map<String,Object>>();
  maps.addAll(payload);

}

Solution 5 - Java

The issue appears with parsing the JSON from request body, tipical for an invalid JSON. If you're using curl on windows, try escaping the json like -d "{"name":"value"}" or even -d "{"""name""":"value"""}"

On the other hand you can ommit the content-type header in which case whetewer is sent will be converted to your String argument

Solution 6 - Java

Jackson Library is what we need for Inject a Json string directly into the response without any extra parsing.

In cases where we have an already-escaped property and need to serialize it without any further escaping, we may want to use Jackson’s @JsonRawValue annotation on that field.

also in its documents has pointed :

> Marker annotation that indicates that the annotated method or field > should be serialized by including literal String value of the property > as is, without quoting of characters. This can be useful for injecting > values already serialized in JSON or passing javascript function > definitions from server to a javascript client. Warning: the resulting > JSON stream may be invalid depending on your input value.

Here is my DTO class:

public class UserProfileResponse {

    private Long id;

    private String userName;

    @JsonRawValue
    private String profile;

    @JsonRawValue
    private String settings;
}

without using @JsonRawValue my response would be something as below, because my profile and settings fields are fetching as JSON from my Database(Oracle for example), and spring serialize them again when wants to create a response for client:

{
    "id": 25,
    "userName": "admin",
    "profile": "{ \"gender\": \"male\", \"nationalId\": \"0123456789\", \"contacts\": { \"phone\": \"02112341234\" } }",
    "settings": "{\"lang\":\"fa-IR\",\"cols\":[],\"watch_lists\":{\"list_2\":[\"2\",\"3\"],\"list_1\":[\"1\",\"2\",\"3\"]}}"
}

but when I place @JsonRawValue on those variables, the response would serialize Like this:

{
    "id": 25,
    "userName": "admin",
    "profile": {
        "gender": "male",
        "nationalId": "0123456789",
        "contacts": {
            "phone": "02112341234"
        }
    },
    "settings": {
        "lang": "fa-IR",
        "cols": [],
        "watch_lists": {
            "list_2": [
                "2",
                "3"
            ],
            "list_1": [
                "1",
                "2",
                "3"
            ]
        }
    }
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionPacificNW_LoverView Question on Stackoverflow
Solution 1 - JavaAndreaView Answer on Stackoverflow
Solution 2 - JavaMateusz StefekView Answer on Stackoverflow
Solution 3 - JavaS. PanView Answer on Stackoverflow
Solution 4 - JavaRusli UsmanView Answer on Stackoverflow
Solution 5 - JavaMaster SlaveView Answer on Stackoverflow
Solution 6 - JavaSobhanView Answer on Stackoverflow