How to transform JSON into object array of a specific class, where this class has composite attributes?

Asked

Viewed 6,183 times

3

I found the following problem when trying to transform a String JSON in object list.

I have the following situation: I have a class Response which has some attributes, among them a list of "Post", where an object of the class "Post" has a list of class objects "Anexo" and I need to get this list right, which I’m not getting right.

OBS: using Gson, the other attributes, which are Strings, were filled out correctly, but when you got on that list I had problems. Actually I think Gson did, but I can’t see the structure.

class Response {
    private List<Post> mPosts;
    // Outros atributos e métodos.
}

I have the following json to fill this list of posts

[
  {
    "post_id": "1",
    "post_descricao": "Meu post 1",
    "post_titulo": "Teste",
    "anexos": [
      {
        "anexo_id": "3",
        "anexo_uri": "img-03.png"
      },
      {
        "anexo_id": "4",
        "anexo_uri": "img-04.png"
      }
    ]
  },
  {
    "post_id": "3",
    "post_descricao": "Meu post 2",
    "post_titulo": "Teste 2",
    " anexos": [
      {
        "anexo_id": "1",
        "anexo_uri": "img-01.png"
      },
      {
        "anexo_id": "2",
        "anexo_uri": "img-02.png"
      }
    ]
  }
]

And I have the classes of posts and attachments:

class Post {
    private int id;
    private String titulo;
    private String descricao;
    private List<Anexo> mAnexos;

    // Outros códigos.
}
class Anexo {
    private int id;
    private String uri;

    // Outros códigos.
}

How to do it properly? I’ve tried enough, Thank you.

  • Edenilton, includes an answer to the question. You managed to solve?

  • Due to the time factor, I was able to solve the problem, maybe it was not a "good practice" to use the conversions manually with objects of the Jsonobject and Jsonarray classes. My goal was to decrease as much as possible the lines of codes and speed up the process of deserialization. Thanks! Next times I will use them calmly. I had a problem when using "Field". I was not compiling. It asked me to change the project build settings: "Change project Compilance and JRE to 1.8.

  • If you have any project configured using json deserializers, please share it with me. thanks!

  • As for the error, it is because the code is in java 8 even, already includes a form with java before the 8 as well. The simplest way is to include the annotation @SerializeName in its same attributes, it is the one that demands the least code.

1 answer

1


Starting from the JSON presented by you and the data structure also presented in the question, if we use a standard approach like this:

final Gson gson = new Gson();
final Response response = gson.fromJson(json, Response.class);

Or this:

final Gson gson = new Gson();
final Type type = new TypeToken<List<Post>>() {}.getType();
final List<Post> posts = gson.fromJson(json, type);
response.setMPosts(posts);

Won’t work.

In the first case an error will be displayed: Expected BEGIN_OBJECT but was BEGIN_ARRAY, why, of course the JSON does not start with an object but rather a vector; in the second, all attributes will be null or with their default value, because Gson does not support by default the nomenclature policy adopted in these objects.

There are some ways to do this and I will show you only one.

  1. use @SerializedName attributes with name outside the supported pattern;
  2. create a custom deserializer. See here how it works: writing an deserializer;
  3. use FieldNamingStrategy and define the nomenclature strategy used in its attributes, a DE-PARA between attributes in java objects and attributes in JSON;
  4. rename the attributes of your objects to reflect some standard approach already supported by Gson. See this link: FieldNamingPolicy;

As you have not been quoted which approach you are already using to deserialize up to the list and Posts, I will show as an example the third approach, creating an own nomenclature strategy.

I am only considering the attributes shown in the question, if there are others that do not follow the pattern you should consider in your implementation.

To have this own strategy we will implement the interface FieldNamingStrategy, the implementation of the method #translate(Field) will be more or less like this:

final String fieldName = f.getName();
if (fieldName.equalsIgnoreCase("mAnexos")) {
    return "anexos";
}

final Class<?> declaringClass = f.getDeclaringClass();
final String className = declaringClass.getSimpleName().toLowerCase();

return className + "_" + fieldName.toLowerCase();

The attribute mAnexos is the only one who does not share the same pattern as the other attributes, so if it is we will return attachments, the name of the attribute in JSON. The others follow the same pattern, that is, the name of the class in which they are in low box separated by a _ of the attribute name.

You can implement the straterium in several ways, an example in Java 8 would be this:

final FieldNamingStrategy strategy = (final Field f) -> {
    final String fieldName = f.getName();
    if (fieldName.equalsIgnoreCase("mAnexos")) {
        return "anexos";
    }

    final Class<?> declaringClass = f.getDeclaringClass();
    final String className = declaringClass.getSimpleName().toLowerCase();

    return className + "_" + fieldName.toLowerCase();
};

In other versions of Java you can implement this way (with anonymous class):

final FieldNamingStrategy strategy = new FieldNamingStrategy() {
    public String translateName(final Field f) {
        final String fieldName = f.getName();
        if (fieldName.equalsIgnoreCase("mAnexos")) {
            return "anexos";
        }

        final Class<?> declaringClass = f.getDeclaringClass();
        final String className = declaringClass.getSimpleName().toLowerCase();

        return className + "_" + fieldName.toLowerCase();
    }
};

After implementing the naming strategy, we can use it this way:

final Gson gson = new GsonBuilder().setFieldNamingStrategy(strategy).create();

final Type type = new TypeToken<List<Post>>() {}.getType();

final List<Post> posts = gson.fromJson(json, type); // substitua "json" pelo o json que você precisa deserializar
final Response response = new Response();
response.setMPosts(posts);

You can also create another object that implements FieldNamingStrategy, is at your discretion.

Browser other questions tagged

You are not signed in. Login or sign up in order to post.