108

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

1
  • You have an error in your pom.xml: an useless/unclosed tag <dependency> before <dependencies>. But is not that your problem... Commented Mar 29, 2015 at 14:26

6 Answers 6

193

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
Sign up to request clarification or add additional context in comments.

5 Comments

This worked! I just tried your second solution... Thank you very much. The issue is that this service is going to have different JSON coming in all the time and it doesn't have a schema... Would Map<String, Object> and also String payload be able to accommodate any type of JSON correctly?
@socal_javaguy The first solution will parse any JSON for you into a Map object. If you are using the second solution will be your care to validate and parse the string to a JSON, for example using this: baeldung.com/jackson-json-to-jsonnode
I'm getting 404 error when the JSON contains Long data type stackoverflow.com/questions/64382824/…
I have a similar controller, but got another problem I got undefined chars when the string comes with special characters like ç ou õ, tried to set the charset with consumes = "text/plain; charset=utf-8", but no luck. Anyone?
@Roberto Junior Perhaps try base64 encoding your input. So your String payload will need to be decoded on receiving it.
25

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

3 Comments

like a charm! tks
I'm getting this error with this approach: org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.fasterxml.jackson.databind.JsonNode]: Is it an abstract class?; nested exception is java.lang.InstantiationException at org.springframework.beans.BeanUtils.instantiateClass(BeanUtils.java:215) ~[spring-beans-5.3.18.jar:5.3.18] Suppressed: reactor.core.publisher.FluxOnAssembly$OnAssemblyException: Do you know what could be the problem?
@LuísCunha I just make a simple example and this works. Could you post your code?
16

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

}

Comments

5

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

}

Comments

2

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

5 Comments

Thanks Master Slave, the reason I didn't use a POJO is because this service is going to receive different types of JSON (containing different content)... Do you understand what I am trying to ask?
I understand (now). On the other hand, your server side is in fact OK. Its complaining about not being able to parse json, editing an answer
I am using bash from OS X Mavericks... What I am trying to do is pass in a JSON so I can parse it to return a custom response whether its valid or not. This is the first step is to first get a JSON string from the Post. Am I doing it wrong?
mhm, well, if I understand you correctly, not really. The fact that you send a content-type header set to application/json means that the conversion will be attempted before the request reaches handler method, and for invalid json this will fail with bad request 400. But, what you should do is send without the content-type header, than the request will reach your method, and there you can programatically try the conversion and return the appropriate response. Not sure if this is a viable approach for you, but its one that we'll work for sure
without the content type it returned this: %7B%22name%22%3A%22value%22%7D=
1

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"
            ]
        }
    }
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.