Skip to content

11 Gson

1. Introducción

Puede darse la situación de que en nuestra aplicación Java, necesitemos atender peticiones representadas en JSON, transformarlas a Java, tratar los datos y devolver una respuesta en JSON. Los servicios REST o los Websockets son un buen ejemplo de esto.

Para resolver este problema podemos implementar «a mano» la lógica de negocio para serializar y deserializar nuestro JSON, lo que supondrá un esfuerzo considerable. O podríamos hacer uso de alguna librería diseñada para este propósito como puede ser Gson.

Gson

Antes de nada necesitaremos añadir la siguiente dependencia a nuestra aplicación:

<dependency>
    <groupid>com.google.code.gson</groupid>
    <artifactid>gson</artifactid>
    <version>2.2.2</version>
</dependency>

También se puede descargar desde aquí

El uso de esta librería se basa en el uso de una instancia de la clase Gson. Dicha instancia se puede crear de manera directa (new Gson()) para transformaciones sencillas o de forma más compleja con GsonBuilder para añadir distintos comportamientos. Lo veremos en los ejemplos.

Una instancia de la clase Gson no mantiene ningún tipo de estado, por lo que el mismo objeto puede reutilizarse para múltiples serializaciones/deserializaciones.

Serializando objetos

En este ejemplo, tenemos una clase Empleado que queremos convertir en un JSON.

public class Employee {
    private final int id;
    private final String name;
    private final String business;

    public Employee(int id, String name, String business) {
        this.id = id;
        this.name = name;
        this.business = business;
    }
}
public class ToJson {
    public void show() {
        final Employee employee = new Employee(32, "Marta", "Avon");
        final Gson gson = new Gson();
        final String json = gson.toJson(employee);

        System.out.println(json);
    }

    public static void main(String[] args) {
        new ToJson().show();
    }
}
{"id":32,"name":"Marta","business":"Avaon"}

Deserializando objetos

Con el método fromJson podemos pasar una cadena JSON a un objeto de una clase específica.

public class FromJson {
    public void show() {
        final String json = "{\"id\":32,\"name\":\"Marta\",\"business\":\"Avon\"}\n";
        final Gson gson = new Gson();
        final Employee employee = gson.fromJson(json, Employee.class);

        System.out.println(employee);
    }

    public static void main(String[] args) {
        new FromJson().show();
    }
}

5. GsonBuilder

A veces, usando el constructor de la clase Gson no podemos tener configuraciones especificas. La librería Gson tiene una clase que sigue el patrón de diseño builder. A un objeto de esta clase, puede llamar a métodos para configurar la clase Gson y finalmente al método create() que es la que creara el objeto Gson con las configuraciones realizadas.

Por ejemplo, si tuviésemos que almacenar, varios datos, la cadena de salida predeterminada por el objeto, no es visualmente la más conveniente, por ello la clase GsonBuilder, tiene un método setPrettyPrinting() que sirve para que la cadena de salida, salga formateada.

public class PrettyPrinting {
    public void show() {
        final Employee employee = new Employee( 32, "Marta", "Avon");
        final Gson gson = new GsonBuilder().setPrettyPrinting().create();
        final String prettyJson = gson.toJson(employee);
        System.out.println(prettyJson);
    }

    public static void main(String[] args) {
        new PrettyPrinting().show();
    }
}
{
  "id": 32,
  "name": "Marta",
  "business": "Avon"
}

Array de Objetos

Tenemos una lista de empleados y queremos serializarla en una cadena JSON, en ese caso, la configuración sigue la misma que todo lo visto anteriormente.

List<Employee> employeeList = List.of(
public class ArrayJSON {
    public void show() {
        List<Employee> employeeList = List.of(
                new Employee( 32, "Marta", "Avon"),
                new Employee(25, "María", "Fresh"),
                new Employee(38, "Juan", "Pop")
        );

        final Gson gson = new GsonBuilder().setPrettyPrinting().create();
        final String json = gson.toJson(employeeList);
        System.out.println(json);
    }

    public static void main(String[] args) {
        new ArrayJSON().show();
    }
}
[
  {
    "id": 32,
    "name": "Marta",
    "business": "Avaon"
  },
  {
    "id": 25,
    "name": "María",
    "business": "Fresh"
  },
  {
    "id": 38,
    "name": "Juan",
    "business": "Pop"
  }
]

Para deserializarlo, será necesario decirle al Gson el tipo de lista que se va a deserializar, por eso al método fromGson(), en vez de pasarle la clase, se le pasará un objeto de clase Type.

La librería Gson contiene la clase TypeToke con la cuál podemos indicar el tipo de lista con el que se va a trabajar.

public class FromArrayJson {
    public void show() {
        final String json = """
    [
    {
        "id": 32,
        "name": "Marta",
        "business": "Avon"
    },
    {
        "id": 25,
        "name": "María",
        "business": "Fresh"
    },
    {
        "id": 38,
        "name": "Juan",
        "business": "Pop"
    }
]""";

        final Gson gson = new Gson();
        final Type typeOfEmployeeList = new TypeToken<List<Employee>>(){}.getType();
        final List<Employee> employeeList = gson.fromJson(json, typeOfEmployeeList);
        System.out.println(employeeList);
    }

    public static void main(String[] args) {
        new FromArrayJson().show();
    }
}

Genéricos

Al igual que las listas, si queremos pasar extraer de JSON una clase genérica, debemos usar la clase TypeToken

public class Generics {
    public void show() {
        final String json = "{\"object\":{\"id\":46,\"name\":\"Miguel\"," +
                "\"business\":\"Autentia\"}}";
        final Type typeWrapper = new TypeToken<Wrapper<Employee>>(){}.getType();
        final Gson gson = new Gson();
        final Wrapper<Employee> wrapperEmployee = gson.fromJson(json, typeWrapper);

        System.out.println(wrapperEmployee);
    }

    public static void main(String[] args) {
        new Generics().show();
    }
}

Fechas

Para tratar con fechas será necesario usar el método de configuración setDateFormat().

public class VacationRequest {
    private final Date init;
    private final Date end;
    private final int totalDays;

    public VacationRequest(Date init, Date end, int totalDays) {
        this.init = init;
        this.end = end;
        this.totalDays = totalDays;
    }
}
public class VacationRequest {
    public void show() {
        final String STRING_FORMATTER = "dd/MM/yyyy";
        final String json = "{\"init\":\"06/08/2012\",\"end\":\"10/08/2012\",\"totalDays\":\"4\"}";
        final Gson gson = new GsonBuilder().setDateFormat(STRING_FORMATTER).create();
        final VacationRequest vacationRequest = gson.fromJson(json, VacationRequest.class);
        System.out.println(vacationRequest);
    }

    public static void main(String[] args) {
        new VacationRequest().show();
    }
}

El problema de este método ocurre en las versiones recientes de Java, no es posible usando este método en las fechas de Java 8, por lo que será necesario parsearlo.

Mapeando propiedades

Realizamos la deserialización del objeto JSON al objeto Java directamente con fromJson, ésta se realizará propiedad a propiedad entre el objeto JSON y el Java de manera automática si las propiedades y atributos tienen el mismo nombre. Sin embargo, podemos hacer que esto no sea así.

Para poder mapear dichas propiedades podemos usar en la clase la anotación SerializedName.

@SerializedName("d")
private final int totalDias;

Objeto Java con una lista

Este parece un caso especial, y que habría que indicar el tipo de la lista, pero en este caso, no es necesario realizar esto, se haría exactamente igual:

public class EmployeeVacations {
    private final int id;
    private final String name;
    private final String business;
    private final List<VacationRequest> vacation;

    public EmployeeVacations(int id, String name, String business, List<VacationRequest> vacation) {
        this.id = id;
        this.name = name;
        this.business = business;
        this.vacation = vacation;
    }

    @Override
    public String toString() {
        return "EmployeeVacations{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", business='" + business + '\'' +
                ", vacation=" + vacation +
                '}';
    }
}
public class ObjectWithList {
    public void show() {
        final String json = """
{
    "id" : 46,
    "name" : "Miguel",
    "business" : "Autentia",
    "vacation":[
        {
            "init" : "06/08/2012",
            "end" : "10/08/2012",
            "d" : 5
        },
        {
            "init" : "23/08/2012",
            "end" : "29/08/2012",
            "d" : 7
        }
    ]
}""";
        final Gson gson = new GsonBuilder().setDateFormat("dd/MM/yyyy").create();
        final EmployeeVacations employee = gson.fromJson(json, EmployeeVacations.class);

        System.out.println(employee);
    }

    public static void main(String[] args) {
        new ObjectWithList().show();
    }
}

Obtener JSON de un fichero

Nuestro objeto Gson no solo acepta un String como representación JSON del objeto a deserializar. También puede leerlo desde un fichero y realizar la mista tarea. Imaginemos que tenemos un fichero employee.json con el objeto JSON del ejemplo anterior. Lo único que tenemos que hacer es pasar a nuestro objeto Gson un Reader (java.io.Reader) con la información de ese fichero y Gson hará el resto.