'How to return a marshalled JSON that has a nested property?

Fellow programmers from SO!

I am building a simple Spring Boot example just to add a Client using a RESTful API. I have written this Controller:

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/cliente")
public class ClienteController {
    
    @PostMapping("/add")
    public Cliente add(@RequestParam(value = "nome", defaultValue = "") String nome, 
            @RequestParam(value = "logradouro", defaultValue = "") String logradouro,
            @RequestParam(value = "CEP", defaultValue = "") String CEP,
            @RequestParam(value = "bairro", defaultValue = "") String bairro,
            @RequestParam(value = "complemento", defaultValue = "") String complemento,
            @RequestParam(value = "municipio", defaultValue = "") String municipio,
            @RequestParam(value = "UF", defaultValue = "") String UF)
    {

        return new Cliente(nome,logradouro,complemento,bairro,municipio,UF,CEP);
    }
}

The Cliente class has an attribute which is an object from another class Endereco. Both classes are depicted below:

Cliente.java

import java.util.UUID;

public class Cliente {
    
    private UUID id;
    private String nome;
    private Endereco endereco;
    
    public Cliente(String nome,String logradouro,String complemento,
            String bairro, String municipio, String UF, String CEP) {
        this.id=UUID.randomUUID();
        this.nome=nome;
        this.endereco=new Endereco(logradouro,complemento,bairro,
                municipio,UF,CEP);
    }
}

Endereco.java

public class Endereco {
    
    private String logradouro;
    private String complemento;
    private String bairro;
    private String minicipio;
    private String UF;
    private String CEP;
    
    public Endereco(String logradouro, String complemento, String bairro, 
            String municipio, String UF, String CEP) {
        this.logradouro=logradouro;
        this.complemento=complemento;
        this.bairro=bairro;
        this.minicipio=municipio;
        this.UF=UF;
        this.CEP=CEP;
    }
    
}

Nevertheless, when I sent a POST request to the API (it is hosted on localhost:8080 for testing purposes), the JSON does not return the endereco property. I supposed it should be a nested JSON object. I am using Python requests to send this POST request:

import requests

url = "http://localhost:8080/api/cliente/add"

payload = {'nome': 'Fabio',
'logradouro': 'Rua dos Bobos',
'CEP': '66666-666',
'bairro': '',
'complemento': '',
'municipio': 'Belem',
'UF': 'PA'}

response = requests.request("POST", url, headers={},
                            data = payload, files = [])

print(response.content)

The result shown does not include the endereco property:

b'{"id":"84956a2b-2a29-435b-a19a-ea6093ab20a1","nome":"Fabio"}'

Is there anything silly that I'm missing?

Thanks for any clue.



Solution 1:[1]

first of all, the method in your controller is not ok. the add method should accept a Cliente object with @RequestBody annotation.

and for your problem, you can use the @JsonUnwrapped which will unrapp the object either in serialization or deserialization.

in the end you will be with those classes :

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/cliente")
public class ClienteController {
    
    @PostMapping("/add")
    public Cliente add(@RequestBody Cliente cliente)
    {

        // do whatever you want with the sent values
    }
}
import java.util.UUID;

public class Cliente {
    
    private UUID id;
    private String nome;
    @JsonUnwrapped
    private Endereco endereco;
    
    public Cliente(String nome,String logradouro,String complemento,
            String bairro, String municipio, String UF, String CEP) {
        this.id=UUID.randomUUID();
        this.nome=nome;
        this.endereco=new Endereco(logradouro,complemento,bairro,
                municipio,UF,CEP);
    }
}

Solution 2:[2]

In Spring MVC, which I guess is your case, the annotation @RequestParam maps query parameters, form data, and parts in multipart requests. In your case, you are sending JSON in the request body, which is not supported by @RequestParam.

This is the mapping of Spring annotation that handles the HTTP request header Content-Type:

  • @RequestParam --> application/x-www-form-urlencoded
  • @RequestBody --> application/json
  • @RequestPart --> multipart/form-data

Having said that either the solution posted already by @Teyeb Med Malek, using DTOs that match your request body and are then converted to your model classes, or changing the body that you use in your request to be the following would work:

{
  'nome': 'Fabio',
  'endereco': {
      'logradouro': 'Rua dos Bobos',
      'CEP': '66666-666',
      'bairro': '',
      'complemento': '',
      'municipio': 'Belem',
      'UF': 'PA'
  }
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Teyeb Med Malek
Solution 2