Home > Software engineering >  Convert list of Objects to JSON Array in java - Spring Boot api testing
Convert list of Objects to JSON Array in java - Spring Boot api testing

Time:12-01

when I try to make a mockmvc post request I have to pass a list of objects in the content tag, the problem is that everytime I try to pass it with this method:

public static String asJsonString(final Object obj) {
        try {
            final ObjectMapper mapper = new ObjectMapper();
            final String jsonContent = mapper.writeValueAsString(obj);
            return jsonContent;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

I get this error:

{"status":"NOT_ACCEPTABLE","errors":{"timestamp":"2021-11-29T11:53:11.2020882Z","message":"Wrong message format","details":"JSON parse error: Cannot deserialize instance of `java.util.ArrayList<Compania>` out of START_OBJECT token; nested exception is com.fasterxml.jackson.databind.exc.MismatchedInputException: Cannot deserialize instance of `java.util.ArrayList<Compania>` out of START_OBJECT token\n at [Source: (PushbackInputStream); line: 1, column: 1]"}}

The test I've made for saving only 1 object works well, but whenever I try to add a list it breaks, here you have my code for testing.

@Test
    void successSavePostCompaniaLista() throws Exception {
        Compania c1 = new Compania("Compania1 list",
            "name1",
            "---",
            "---",
            null,
            null);
        Compania c2 = new Compania("Compania2 list",
            "name2",
            "---",
            "---",
            null,
            null);

        List<Compania> companias = List.of(c1,c2);
        when(companiaRepository.save(any(Compania.class))).then(returnsFirstArg());

        this.mockMvc.perform(
                post("/companias/lista")
                    .header("authorization", "Bearer "   token)
                    .content(asJsonString(companias)) //<-- Here gives me errors
                    .contentType(MediaType.APPLICATION_JSON))
            .andDo(print())
            .andExpect(status().isCreated())
            .andExpect(jsonPath("$.result[0].success[0]").isNotEmpty())
            .andExpect(jsonPath("$.result[0].success[0].name").value(c1.getName()))
            .andExpect(jsonPath("$.result[0].success[1].name").value(c2.getName()));

    }

The one that works is similar to this one, but using only one object.

The http direction and the rest is ok.

Thanks!

Edit. Here is the service I'm trying to test

public Map<String, Object> postListCompanias(List<Compania> companias) {
        for (int i = 0; i < companias.size(); i  ) {
            Compania companiaN = companias.get(i);
            companiaN.setId(null);

            companiaRepository.save(companias.get(i));
            System.out.println(companias.get(i));
        }

        Map<String, Object> mappedResult = Collections.singletonMap(
            "result",
            List.of(
                Collections.singletonMap(
                    "success",
                    companias
                )
            )
        );
        return mappedResult;
    }

And here the controller:

@ResponseStatus(HttpStatus.CREATED)
    @PostMapping("/lista")
    public Map<String, Object> createCompanias(@RequestBody List<Compania> companias) {

        return companiaService.postListCompanias(companias);
    }

CodePudding user response:

It seems to me that the issue is that you are trying to pass something like the following JSON to the content:

[
  {
    "compania1_list": "Compania1 list",
    "name1": "name1",
    "s": "---",
    "s1": "---",
    "o": null,
    "o1": null
  },
  {
    "compania1_list": "Compania2 list",
    "name1": "name2",
    "s": "---",
    "s1": "---",
    "o": null,
    "o1": null
  }
]

This is a JSON Array with 2 JSON Objects and not a JSON Object with a JSON Array with 2 JSON Objects. My guess is that MockHttpServletRequestBuilder.content() method is not expecting JSON like this.

With this being said, I would change your Controller to accept an Object and not a Collection as follows:

@ResponseStatus(HttpStatus.CREATED)
@PostMapping("/lista")
public Map<String, Object> createCompanias(@RequestBody CompaniasCreationRequest companiasCreationRequest) {
    return companiaService.postListCompanias(companiasCreationRequest.getCompanias());
}

Being CompaniasCreationRequest as follows:

public class CompaniasCreationRequest {
    private List<Compania> companias;

    public CompaniasCreationRequest(List<Compania> companias) {
        this.companias = companias;
    }

    public List<Compania> getCompanias() {
        return companias;
    }

    public void setCompanias(List<Compania> companias) {
        this.companias = companias;
    }
}

In your test this would mean the following changes:

@Test
void successSavePostCompaniaLista() throws Exception {
    Compania c1 = new Compania("Compania1 list",
        "name1",
        "---",
        "---",
        null,
        null);
    Compania c2 = new Compania("Compania2 list",
        "name2",
        "---",
        "---",
        null,
        null);

    CompaniasCreationRequest companiasCreationRequest = new CompaniasCreationRequest(List.of(c1,c2));
    
    when(companiaRepository.save(any(Compania.class))).then(returnsFirstArg());

    this.mockMvc.perform(
            post("/companias/lista")
                .header("authorization", "Bearer "   token)
                .content(asJsonString(companiasCreationRequest))
                .contentType(MediaType.APPLICATION_JSON))
        .andDo(print())
        .andExpect(status().isCreated())
        .andExpect(jsonPath("$.result[0].success[0]").isNotEmpty())
        .andExpect(jsonPath("$.result[0].success[0].name").value(c1.getName()))
        .andExpect(jsonPath("$.result[0].success[1].name").value(c2.getName()));
}

Now your request body will look like the following (which is a somewhat more standard JSON format):

{
  "companias": [
    {
      "compania1_list": "Compania1 list",
      "name1": "name1",
      "s": "---",
      "s1": "---",
      "o": null,
      "o1": null
    },
    {
      "compania1_list": "Compania2 list",
      "name1": "name2",
      "s": "---",
      "s1": "---",
      "o": null,
      "o1": null
    }
  ]
}
  • Related