Returning JSON as Response Spring Boot

15,742

Solution 1

You need to define your json attribute as JsonNode so jackson can read it back and forrward, but mark is as @Transient so JPA does not try to store it on database.

Then you can code getter/setter for JPA, where you translate from JsonNode to String back and forward. You define a getter getJsonString that translate JsonNode json to String. That one can be mapped to a table column, like 'json_string', then you define a setter where you receive the String from JPA and parse it to JsonNode that will be avaialable for jackson, jackson then will translate it to a json object not a string as you mention.

@Entity
@Table(name = "model")
public class SomeModel {

  private Long id;
  private String col1;

  //  Attribute for Jackson 
  @Transient
  private JsonNode json;

  @Id
  @GeneratedValue(strategy = GenerationType.AUTO)
  public Long getId() {
    return id;
  }

  @Column(name ="col1")
  public String getCol1() {
    return col1;
  }

  // Getter and setter for name

  @Transient
  public JsonNode getJson() {
    return json;
  }

  public void setJson(JsonNode json) {
    this.json = json;
  }

  // Getter and Setter for JPA use
  @Column(name ="jsonString")
  public String getJsonString() {
    return this.json.toString();
  }

  public void setJsonString(String jsonString) {
    // parse from String to JsonNode object
    ObjectMapper mapper = new ObjectMapper();
    try {
      this.json = mapper.readTree(jsonString);
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
}

Notice, @Column are defined at gettters because we need to indicate JPA to use getJsonString and JPA requires consistency so all column's getters must be mark with @Columns.

Solution 2

Change your some model class like this

@Entity
@Table(name="some_table")
public class SomeModel {

    @Id
    @Column(name="p_col", nullable=false)
    private Integer id;
    @Column(name="s_col")
    private String name
    @Column(name="t_col")
    private String json;   // this column contains json data

    @Column(name = "t_col", columnDefinition = "json")
    @Convert(attributeName = "data", converter = JsonConverter.class)
    private Map<String, Object> json = new HashMap<>();

    //constructors
    //getters and setters
}

Write a json converter class.

@Converter
public class JsonConverter
                    implements AttributeConverter<String, Map<String, Object>> 
{


    @Override
    public Map<String, Object> convertToDatabaseColumn(String attribute)
    {
        if (attribute == null) {
           return new HashMap<>();
        }
        try
        {
            ObjectMapper objectMapper = new ObjectMapper();
            return objectMapper.readValue(attribute, HashMap.class);
        }
        catch (IOException e) {
        }
        return new HashMap<>();
    }

    @Override
    public String convertToEntityAttribute(Map<String, Object> dbData)
    {
        try
        {
            ObjectMapper objectMapper = new ObjectMapper();
            return objectMapper.writeValueAsString(dbData);
        }
        catch (JsonProcessingException e)
        {
            return null;
        }
    }
}

It will convert database json attribute to your desire results. Thanks

Solution 3

In your controller add json response:

 @RequestMapping(value = "/getsome", method = RequestMethod.GET, produces = "application/json"

And wrap the getData string as response

    public class StringResponse {

        private String response;

        public StringResponse(String s) { 
           this.response = s;
        }

}
Share:
15,742
404or505
Author by

404or505

Updated on June 14, 2022

Comments

  • 404or505
    404or505 almost 2 years

    I am trying to get rest response as json instead I am getting as string.

    Controller

    @RestController
    @RequestMapping("/api/")
    public class someController{
    
      @Autowired
      private SomeService someService;
    
      @GetMapping("/getsome")
      public Iterable<SomeModel> getData(){
        return someService.getData();
      }
    }
    

    Service

    @Autowired
    private SomeRepo someRepo;
    
    public Iterable<someModel> getData(){
      return someRepo.findAll();
    }
    

    Repository

    public interface SomeRepo extends CrudRepository<SomeModel,Integer>{
    
    }
    

    Models

    @Entity
    @Table(name="some_table")
    public class SomeModel{
    
      @Id
      @Column(name="p_col", nullable=false)
      private Integer id;
      @Column(name="s_col")
      private String name
      @Column(name="t_col")
      private String json;   // this column contains json data
    
      //constructors, getters and setters
    }
    

    when I run localhost:8080/api/getsome I am getting:

    [
     {
        "p_col":1,
        "s_col":"someName",
        "t_col":" 
    {\r\n\t"school_name\":\"someSchool\",\t\r\n\t"grade\":"A\",\r\n\t\"class\": 
     [{\"course\":"abc",\t"course_name\":\"def" }]}"
      }
    ]
    

    Field t_col is returning string instead of json. How do I get json objects in response?

    As for the database, the three columns are int, varchar and varchar.

    Any help would be appreciated. Thanks !!

  • 404or505
    404or505 about 5 years
    Thank you, it worked for me !! How does it work the other way around, if I need to save json input as string in db?
  • Cristian Colorado
    Cristian Colorado about 5 years
    That part is incorporated in the solution, you see when JPA is trying to translate the POJO to an insert statement, it will execute the getJsonString method, as it is marked with @Column liked to jsonString column. So as long as you have json/jsonString getters/setters like this it will work like you expected.
  • 404or505
    404or505 about 5 years
    I tried various ways using JsonNode to save raw Json objects to string but couldn;t get it to work. I am getting following error "status": 400, "error": "Bad Request", "message": "JSON parse error: Cannot deserialize instance of java.util.ArrayList out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of java.util.ArrayList out of START_OBJECT token\n at [Source: (PushbackInputStream); line: 1, column: 1]", "trace": "org.springframework.http.converter.HttpMessageNotReadableEx‌​ception: JSON parse e
  • Cristian Colorado
    Cristian Colorado about 5 years
    You need to be more specific on the implementation you are trying to make, in the example you provide there is no element with array.
  • 404or505
    404or505 about 5 years
    For the POST mapping I am providing the following controller: @PostMapping("/saverawjson") public void saveRawJson(@RequestBody SomeModel someModel){ //..... someRepository.save(someModel) } I am using following for repo public interface SomeRepo extends CrudRepository<SomeModel,Integer>{}
  • 404or505
    404or505 about 5 years
    Update: there are no errors, it was my mistake to provide wrong json but the column (string) with json is populating as null.
  • zar3bski
    zar3bski about 3 years
    Interesting quick fix but I endup with error 406 while calling my APIs. What is it waiting for exactly?