3

i have the following Json string, which I'm suppose to deserialize. The problem is: since this string comes from a server I can't change it and I need to deserialize as POJO. You can see that the Grafs keys have different values for each subarea. One is an object an the other an array. How can I handle this?

{
"Status": "true",
"Result: {
    "rows": {
        "row": {
            "status": true,
            "subareas": [
                {
                    "nome": "Associacao Utente",
                    "id": 9,
                    "grafs": {
                        "rows": {
                            "id": 6,
                            "nome": "Associacao Utente",
                            "tipo": "PIE",
                            "serv": "MV_AS_UTENTE_POR_NEGOCIO",
                            "periodo": "ANO"
                        }
                    }
                }, {
                    "nome": "Chaves",
                    "id": 60,
                    "grafs": {
                        "rows": [
                            {
                                "id": 35,
                                "nome": "Chaves Criados por ano",
                                "tipo": "LINHA",
                                "serv": "MV_ASSOC_TOTAL_CHAVES",
                                "periodo": "ANO"
                            }, {
                                "id": 592,
                                "nome": "Chaves Associado Ao User Portal",
                                "tipo": "BAR",
                                "serv": "MV_ASSOC_USER_CHAVES",
                                "periodo": "TODOS"
                            }, {
                                "id": 593,
                                "nome": "Chaves Associado Ao Negocios",
                                "tipo": "BAR",
                                "serv": "MV_ASSOC_CHAVES",
                                "periodo": "TODOS"
                            }
                        ]
                    }
                }
            ]
        }
    }
}

}

Here follows my classes.

public class Example {
    private String Status;
    private Result Result;

    public String getStatus() {
        return Status;
    }

    public void setStatus(String status) {
        Status = status;
    }

    public Result getResult() {
        return Result;
    }

    public void setResult(Result result) {
        Result = result;
    }

    @Override
    public String toString() {
        return "Example [Status=" + Status + ", Result=" + Result + "]";
    }

}

public class Result {
    private Rows rows;

    public Rows getRows() {
        return rows;
    }

    public void setRows(Rows rows) {
        this.rows = rows;
    }

    @Override
    public String toString() {
        return "Result [rows=" + rows + "]";
    }
}

public class Grafs {
    private List<Rows_> rows = new ArrayList<>();

    public List<Rows_> getRows() {

        return rows;
    }

    public void setRows(List<Rows_> Rows) {
        this.rows = Rows;
    }


    @Override
    public String toString() {
        return "Grafs [rows=" + rows + "]";
    }
}

public class Row {
    private Boolean status;
    private List<Subarea> subareas = new ArrayList<>();

    public Boolean getStatus() {
        return status;
    }

    public void setStatus(Boolean status) {
        this.status = status;
    }

    public List<Subarea> getSubareas() {
        return subareas;
    }

    public void setSubareas(List<Subarea> subareas) {
        this.subareas = subareas;
    }

    @Override
    public String toString() {
        return "Row [status=" + status + ", subareas=" + subareas + "]";
    }
}

public class Row_ {
    private Integer id;
    private String nome;
    private String serv;
    private String periodo;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public String getServ() {
        return serv;
    }

    public void setServ(String serv) {
        this.serv = serv;
    }

    public String getPeriodo() {
        return periodo;
    }

    public void setPeriodo(String periodo) {
        this.periodo = periodo;
    }

    @Override
    public String toString() {
        return "Row_ [id=" + id + ", nome=" + nome + ", serv=" + serv
               + ", periodo=" + periodo + "]";
    }
}

public class Rows {
    private Row row;

    public Row getRow() {
        return row;
    }

    public void setRow(Row row) {
        this.row = row;
    }

    @Override
    public String toString() {
        return "Rows [row=" + row + "]";
    }
}

public class Rows_ {
    private Row_ row;

    public Row_ getRow() {
        return row;
    }

    public void setRow(Row_ row) {
        this.row = row;
    }
}

public class Subarea {
    private String nome;
    private Integer id;

    private Grafs grafs;

    public String getNome() {
        return nome;
    }

    public void setNome(String nome) {
        this.nome = nome;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Grafs getGrafs() {
        return grafs;
    }

    public void setGrafs(Grafs grafs) {
        this.grafs = grafs;
    }

    @Override
    public String toString() {
        return "Subarea [nome=" + nome + ", id=" + id + ", grafs=" + grafs
               + "]";
    }
}

Using these classes I'm getting the following error:

Expected BEGIN_ARRAY  but was BEGIN_OBJECT at line 13 column 18.

I declared Rows_ as an arraylist and it encounters an object instead. But the second Rows_ is an array indeed. How can i address this?

Arrays with one element should still be rendered as arrays. That's why I used arrays. But it's giving the error I described.

Thanks for your help. I really appreciate it.

yadav_vi
  • 1,289
  • 4
  • 16
  • 46
Rui Sousa
  • 53
  • 9
  • Did the solution given below work for you? I have a [similar question](http://stackoverflow.com/q/35146630/2863440). – yadav_vi Feb 02 '16 at 10:57

1 Answers1

1

You can use TypeAdapterFactory to do the conversion. Here is a factory that will add that functionality to all of your List member types --

import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import java.io.IOException;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Collections;
import java.util.List;

public class SingletonListTypeAdapterFactory implements TypeAdapterFactory {
     public <T> TypeAdapter<T> create(Gson gson, TypeToken<T> typeToken) {

       Type type = typeToken.getType();
       if (typeToken.getRawType() != List.class
           || !(type instanceof ParameterizedType)) {
         return null;
       }
       Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0];
       TypeAdapter<?> elementAdapter = gson.getAdapter(TypeToken.get(elementType));
       TypeAdapter<T> arrayAdapter = gson.getDelegateAdapter(this, typeToken);
       return (TypeAdapter<T>) newSingtonListAdapter((TypeAdapter<Object>) elementAdapter, (TypeAdapter<List<Object>>) arrayAdapter);
     }

     private <E> TypeAdapter<List<E>> newSingtonListAdapter(
         final TypeAdapter<E> elementAdapter,
         final TypeAdapter<List<E>> arrayAdapter) {
       return new TypeAdapter<List<E>>() {

         public void write(JsonWriter out, List<E> value) throws IOException {
           if(value == null || value.isEmpty()) {
             out.nullValue();
           } else if(value.size() == 1) {
            elementAdapter.write(out, value.get(0));
           } else {
             arrayAdapter.write(out, value);
           }
         }

         public List<E> read(JsonReader in) throws IOException {
           if (in.peek() != JsonToken.BEGIN_ARRAY) {
             E obj = elementAdapter.read(in);
             return Collections.singletonList(obj);
           }
           return arrayAdapter.read(in);
         }
       };
     }
   }

As bonus, it also serializes in the same way, if needed. If you also want to serialize as array, replace the write method with a call to arrayAdapter.write.

To you, add to your gson when building --

Gson gson = new GsonBuilder().registerTypeAdapterFactory(new SingletonListTypeAdapterFactory())
        .create();
iagreen
  • 31,470
  • 8
  • 76
  • 90
  • using TypeAdapterFactory it is giving this output when i run: run: gson2.Example@caf0ed BUILD SUCCESSFUL (total time: 0 seconds) – Rui Sousa Oct 08 '15 at 06:27
  • Of course, but i have this to print me the converted Json: Gson gson = builder.registerTypeAdapterFactory(new SingletonListTypeAdapterFactory()).create(); Example example = gson.fromJson(json, Example.class); System.out.println(example); it should've printed the converted Json and not Example@caf0ed, right? – Rui Sousa Oct 08 '15 at 06:49
  • It should print in the logcat if on android, or the "run" window in IntelliJ IDEA. The message you gave is in the "Messages" window. Can you print a message before you call `fromJson` to confirm you are getting output? – iagreen Oct 08 '15 at 07:04
  • I forgot to mention this. Coping and pasting your code it gave error at theses lines Type type = typeToken.getType(); if (typeToken.getRawType() != List.class || !(type instanceof ParameterizedType)) { return null; } Type elementType = ((ParameterizedType) type).getActualTypeArguments()[0]; TypeAdapter> elementAdapter = gson.getAdapter(TypeToken.get(elementType)); It is saying incompatible types, Required java.lang.ProcessBuilder.Redirect.Type and found java.lang.reflect.type. I fixed the imports and still this error. – Rui Sousa Oct 08 '15 at 10:10
  • what i need to do is fix the json string programmatically using string methods(i guess) to uniformize it. Any clues on how to do it? – Rui Sousa Oct 08 '15 at 19:08
  • `java.lang.ProcessBuilder.Redirect.Type` this is not used, need to check your imports. Will edit about. – iagreen Oct 08 '15 at 20:43
  • Added my imports above – iagreen Oct 08 '15 at 20:44