Gson: Expected begin_array but was STRING how to control this

59
June 13, 2019, at 4:20 PM

Im learning how to produce and consume JSON in rest services, but I wanna learn it well so im trying all possible cases of objects, one of them is an object that has an List attribute like this class:

import java.util.List;
public class PruebaJSON {
    private String nombre;
    private List atributos;
    private String descripcion;
    public String getNombre() {
        return nombre;
    }
    public void setNombre(String nombre) {
        this.nombre = nombre;
    }
    public List getAtributos() {
        return atributos;
    }
    public void setAtributos(List atributos) {
        this.atributos = atributos;
    }
    public String getDescripcion() {
        return descripcion;
    }
    public void setDescripcion(String descripcion) {
        this.descripcion = descripcion;
    }   
}

Then all what im doing on my rest service method is this:

@POST
@Path("/prueba")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public PruebaJSON prueba(String data) {
    try {
        JSONObject json = new JSONObject(data);

        Gson convertir = new GsonBuilder().create();
        PruebaJSON pruebaJson = convertir.fromJson(json.toString(), PruebaJSON.class);
        return pruebaJson;
    } catch (Exception e) {
        System.out.println("error " + e);
        return null;
    }
}

Then in POSTMAN I pass this:

{
    "descripcion": "Primera prueba",
    "nombre": "Prueba 1",
    "atributos": [
        "hello",
        "kek",
        "lul"
    ]
}

And it works fine, the problem is when I try to do the same by Java, for example:

List atributos = new ArrayList<>();
atributos.add("hello");
atributos.add("kek");
atributos.add("lul");
System.out.println(bus.prueba("Prueba 1", "Primera Prueba", atributos));

bus.prueba just executes the service but then in console I get this error:

14:16:56,567 INFO  [stdout] (default task-2) error com.google.gson.JsonSyntaxException: java.lang.IllegalStateException: Expected BEGIN_ARRAY but was STRING at line 1 column 66 path $.atributos

I did the search of the error and found this: Gson: Expected begin_array but was STRING

I understand the error but whats the solution? I can't really control how the JSON builds the arraylist can I? This is the method prueba in my client:

public String prueba(String nombre, String descripcion, List atributos) {
        HashMap map = new HashMap<>();
        map.put("nombre", nombre);
        map.put("descripcion", descripcion);
        map.put("atributos", atributos);
        String respuesta = utilidadesRestSeguridad.consumir("prueba", map);
        return respuesta;
    }

In my client component this is the method that builds the json:

public static JsonObject generateJSON(HashMap map) throws MalformedURLException {
    JsonObject json = new JsonObject();
    for (Object key : map.keySet()) {
        json.addProperty(key.toString(), map.get(key).toString());
    }
    return json;
}

And thats it guys if you wanna see more code or me to explain something tell me I appreciate any help.

I think maybe the error is in the method generateJSON because of the .toString(), but then how I should handle that case?

Answer 1

Assuming that the line utilidadesRestSeguridad.consumir("prueba", map) ends up calling your generateJSON method downstream, then your issue is likely in the generateJSON() method as you suspect. Basically, you are just adding all elements as strings. If one of the elements in your map is an instance of a List, then you need to call JsonObject#add("atributos", value). For example, you will need something like the following code:

if (map.get(key) instanceof List) {
    json.add(key.toString(), map.get(key);
} else {
    json.addProperty(key.toString(), map.get(key).toString());
}
Answer 2

As I suspected, the error was in the generateJSON method, needed to add this validation that entpnerd suggested:

public static JsonObject generateJSON(HashMap map) throws MalformedURLException {
    JsonObject json = new JsonObject();
    for (Object key : map.keySet()) {
        if (map.get(key) instanceof List) {
            JsonParser parser = new JsonParser();
            parser.parse((map.get(key).toString()));
            json.add(key.toString(), parser.parse((map.get(key).toString())));
        } else {
            json.addProperty(key.toString(), map.get(key).toString());
        }
    }
    return json;
}

Notice that I had to use JsonParser, not sure how is it working but at the end that made it work.

Source: How to parse this JSON String with GSON?

Anyways Im gonna try the solution entpnerd is suggesting and post it too.

Here is the implementation of entpnerd suggestion:

public static JsonObject generateJSON(HashMap map) throws MalformedURLException {
    JsonObject json = new JsonObject();
    for (Object key : map.keySet()) {
        if (map.get(key) instanceof List) {
            JsonArray jsonArray = new JsonArray();
            for (Object object : (ArrayList<Object>) map.get(key)) {
                jsonArray.add(object.toString());
            }
            json.add(key.toString(), jsonArray);
        } else {
            json.addProperty(key.toString(), map.get(key).toString());
        }
    }
    return json;
}

it works too, you guys decide which one to use, thanks very much.

My only question is, what if the element that is an array, has more arrays inside it, what would you do?

Answer 3

You don't need to get that json value manually, add requestbody annotation to your method parameter

public PruebaJSON prueba(@RequestBody PruebaJSON json){
    System.out.println(json);
};
READ ALSO
How to solve linear equation systems in Java? [on hold]

How to solve linear equation systems in Java? [on hold]

I am developing a solver for truss systems (for mechanical and civil engineers)In this software, I need a linear equation solver

56
How to split list

How to split list

I've got an ArrayList which is filled from input file line by lineOne line is one index here, so I need to split that by words because I dont want that

45
@AfterClass method don&#39;t finish the testcase

@AfterClass method don't finish the testcase

I'm testing my databseI'm using the @BeforeClass method to delete everything in my databasetable then inserting some data

34
Android.graphics.rect - always null

Android.graphics.rect - always null

I want to write some tests for my Android project - and I‘m trying to instantiate a Rect()-Object in a JUnit test - but I’m always getting a null instead of the objectHere some examples:

45