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

273,454

Solution 1

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

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

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

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

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

Share:
273,454

Related videos on Youtube

PacificNW_Lover
Author by

PacificNW_Lover

Updated on October 02, 2021

Comments

  • PacificNW_Lover
    PacificNW_Lover over 2 years

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

    • Andrea
      Andrea about 9 years
      You have an error in your pom.xml: an useless/unclosed tag <dependency> before <dependencies>. But is not that your problem...
  • PacificNW_Lover
    PacificNW_Lover about 9 years
    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?
  • Master Slave
    Master Slave about 9 years
    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
  • PacificNW_Lover
    PacificNW_Lover about 9 years
    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?
  • Master Slave
    Master Slave about 9 years
    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
  • PacificNW_Lover
    PacificNW_Lover about 9 years
    without the content type it returned this: %7B%22name%22%3A%22value%22%7D=
  • PacificNW_Lover
    PacificNW_Lover about 9 years
    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?
  • Andrea
    Andrea about 9 years
    @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
  • vishal sundararajan
    vishal sundararajan over 3 years
    I'm getting 404 error when the JSON contains Long data type stackoverflow.com/questions/64382824/…
  • Joao Moreno
    Joao Moreno over 3 years
    like a charm! tks
  • Riberto Junior
    Riberto Junior about 2 years
    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?
  • Luís Cunha
    Luís Cunha about 2 years
    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(BeanUti‌​ls.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?
  • LiberiFatali
    LiberiFatali about 2 years
    @LuísCunha I just make a simple example and this works. Could you post your code?