Gson — una biblioteca de Google para serializar objetos Java a JSON y viceversa, ampliamente utilizada en el desarrollo de Android. Permite convertir grafos complejos de objetos en cadenas JSON compactas sin escribir analizadores manualmente. Según Google Gson, 2024, la biblioteca tiene más de 23 mil estrellas en GitHub y sigue siendo una de las soluciones más populares para trabajar con JSON en el ecosistema Java y Kotlin.
Puntos Clave
Gson es una biblioteca Java desarrollada por Google para convertir objetos a representación JSON y viceversa. Utiliza reflexión para analizar la estructura de las clases, lo que permite trabajar sin configuración previa. Gson admite objetos Java arbitrarios, colecciones, arrays, genéricos y clases anidadas. La biblioteca no requiere anotaciones para el uso básico, pero las proporciona para un ajuste fino. El principal inconveniente de la reflexión es la reducción del rendimiento durante la inicialización y la imposibilidad de optimizar en tiempo de compilación, lo que es especialmente notable en el arranque en frío de una aplicación Android al deserializar cientos de modelos. A pesar de esto, Gson sigue siendo una opción fiable para la mayoría de los proyectos gracias a su estabilidad y extensa documentación.
Gson fue lanzado por Google en 2008 y rápidamente se convirtió en el estándar de facto para JSON en aplicaciones Android. Antes de la llegada de Moshi y kotlinx.serialization, Gson era la única opción popular para proyectos Kotlin. Facilidad de integración — añadir una sola dependencia en build.gradle — y la ausencia de anotaciones obligatorias hicieron que Gson fuera popular entre desarrolladores de todos los niveles.
// Agregar Gson en build.gradle
dependencies {
implementation 'com.google.code.gson:gson:2.10.1'
}
// Uso básico
data class User(
val id: Int,
val name: String,
val email: String
)
val gson = Gson()
val user = User(1, "John", "john@test.com")
val json = gson.toJson(user)
println(json) // {"id":1,"name":"John","email":"john@test.com"}
Además de la serialización básica, Gson proporciona GsonBuilder para configurar el comportamiento: formato de fechas, desactivación del escape HTML, formato de claves e instancias personalizadas. GsonBuilder también permite registrar JsonSerializer y JsonDeserializer personalizados para tipos que la biblioteca no puede manejar automáticamente. La flexibilidad de configuración convierte a GsonBuilder en una herramienta indispensable y útil al adaptar la biblioteca a los requisitos específicos del proyecto en el desarrollo moderno de Android.
toJson convierte un objeto Java en una cadena JSON analizando sus campos mediante reflexión. Por defecto, Gson incluye todos los campos excepto transient y static. El método admite cualquier tipo: primitivos, objetos, colecciones y arrays. fromJson realiza la operación inversa, aceptando una cadena JSON y la clase del objeto destino, y devuelve una instancia con los campos completados.
Durante la serialización, Gson recorre recursivamente todos los campos del objeto, incluidos los anidados. Las referencias cíclicas provocan StackOverflowError, por lo que deben excluirse mediante la anotación @Expose o un adaptador personalizado. Para las colecciones, Gson conserva los tipos de elementos, pero al deserializar una lista con genéricos se requiere TypeToken para preservar la información del tipo.
// data class con objeto anidado
data class Address(
val city: String,
val street: String
)
data class Employee(
val id: Int,
val name: String,
val address: Address
)
val gson = Gson()
val employee = Employee(1, "Alice",
Address("New York", "5th Ave"))
// Serialización a JSON
val json = gson.toJson(employee)
// Deserialización desde JSON
val jsonString = """
{"id":2,"name":"Bob","address":{"city":"London","street":"Baker St"}}
"""
val parsed = gson.fromJson(jsonString, Employee::class.java)
Gson proporciona un conjunto de anotaciones para gestionar el proceso de serialización. @SerializedName especifica el nombre de la clave JSON que difiere del nombre del campo. @Expose controla si un campo se incluye en la serialización: Gson creado mediante GsonBuilder.excludeFieldsWithoutExposeAnnotation() solo procesará los campos con @Expose. @Since y @Until controlan el versionado de campos.
La anotación @SerializedName resuelve el problema de la falta de concordancia de nombres: el servidor puede usar snake_case mientras que en el código se usa camelCase. La anotación acepta un valor y alternativas opcionales para la retrocompatibilidad. @Expose permite ocultar campos sensibles (contraseñas, tokens) de la serialización marcándolos como @Expose(serialize = false). Además de la inclusión y exclusión, @Expose se puede combinar con GsonBuilder.excludeFieldsWithoutExposeAnnotation para crear una lista blanca de campos, lo que ayuda a controlar la superficie de ataque al serializar objetos con muchos campos.
// Modelo con anotaciones Gson
data class UserResponse(
@SerializedName("user_id")
val userId: Int,
@SerializedName("full_name",
alternate = [Alternative("name")])
val fullName: String,
@Expose(serialize = false)
val password: String
)
// Gson con filtrado @Expose
val gson = GsonBuilder()
.excludeFieldsWithoutExposeAnnotation()
.setPrettyPrinting()
.create()
val user = UserResponse(1, "John", "secret123")
println(gson.toJson(user))
// {"user_id":1,"full_name":"John"} — password excluido
El problema de los genéricos en Java y Kotlin es el borrado de tipos en tiempo de compilación. Cuando Gson deserializa List<User>, no conoce el tipo de elemento y devuelve List<Map<String, Any>>. Para preservar la información del tipo, Gson proporciona TypeToken — una clase abstracta que captura el parámetro de tipo a través de una clase anónima. Sin TypeToken, el desarrollador tendría que convertir manualmente cada elemento de Map al tipo destino, lo que resulta en código engorroso y pérdida de rendimiento.
TypeToken resuelve el problema del borrado de tipos. El desarrollador crea una subclase anónima de TypeToken con el parámetro de tipo requerido, y Gson utiliza la información de la firma de la clase para una deserialización correcta. TypeToken también funciona con Map, Set y cualquier otro tipo parametrizado, incluidos los genéricos anidados. En particular, para Map<String, List<User>>, se requiere un TypeToken con la firma completa del tipo anidado; de lo contrario, Gson deserializa los valores como List<Map<String, Any>> en lugar de List<User>.
// TypeToken para deserialización de listas
data class Product(
val id: Int,
val title: String,
val price: Double
)
val jsonArray = """
[
{"id":1,"title":"Phone","price":599.0},
{"id":2,"title":"Laptop","price":1299.0}
]
"""
val gson = Gson()
val listType = object : TypeToken<List<Product>>() {}
val products: List<Product> =
gson.fromJson(jsonArray, listType.type)
// Deserializador personalizado
class LocalDateAdapter :
JsonDeserializer<LocalDate> {
override fun deserialize(
json: JsonElement,
typeOfT: java.lang.reflect.Type,
context: JsonDeserializationContext
): LocalDate {
return LocalDate.parse(json.asString)
}
}
Para la lógica de serialización personalizada, Gson admite las interfaces JsonSerializer y JsonDeserializer. Se registran mediante GsonBuilder.registerTypeAdapter() y permiten manejar tipos que la biblioteca no puede serializar automáticamente: fechas de Java 8, Enums con valores no estándar o clases de terceros sin acceso al código fuente. Al implementar un adaptador, es importante monitorear el rendimiento: llamar a la reflexión dentro de un adaptador personalizado anula las ventajas del control manual, por lo que se prefieren las llamadas directas a métodos y campos. En el ecosistema Gson, también existe el módulo gson-extras, que proporciona adaptadores para tipos comunes como UUID, Optional y las ruedas de fecha Joda-Time.
GsonBuilder proporciona decenas de métodos para el ajuste fino de la serialización. setPrettyPrinting añade sangría y saltos de línea al JSON de salida para facilitar la lectura. disableHtmlEscaping desactiva el escape de caracteres HTML en las cadenas. setDateFormat especifica el formato de fecha, lo que es crítico al trabajar con servidores que utilizan representaciones de tiempo no estándar. setLenient activa el modo de análisis indulgente, que ignora ciertos errores de formato JSON. addDeserializationExclusionStrategy permite excluir campos de la deserialización mediante programación basándose en estrategias personalizadas. Para la depuración, setPrettyPrinting combinado con el registro es útil: hace que las respuestas JSON sean legibles en los registros y simplifica la búsqueda de discrepancias.
Una característica importante de GsonBuilder es la gestión de versiones de campos mediante las anotaciones @Since y @Until. El desarrollador especifica la versión del objeto a través de setVersion, y Gson incluye o excluye automáticamente los campos según su anotación de versión. Esto es útil durante la evolución de la API, cuando el mismo modelo se utiliza para diferentes versiones del protocolo del servidor. GsonBuilder también admite el registro de TypeAdapterFactory para el manejo global de tipos de familia y complexMapKeySerialization para trabajar correctamente con claves complejas de Map.
Preguntas Frecuentes
Gson es una biblioteca de Google para convertir objetos Java a JSON y viceversa. Se utiliza ampliamente en aplicaciones Android para analizar respuestas del servidor, serializar solicitudes y almacenar datos en el almacenamiento local.
Por defecto, Gson omite los campos null durante la serialización. Para incluir valores null, use GsonBuilder.serializeNulls(). Durante la deserialización, los campos que faltan en JSON permanecen como null o toman el valor predeterminado del tipo.
Moshi no utiliza reflexión para las clases Kotlin, lo que proporciona un mayor rendimiento y un comportamiento predecible. Moshi también maneja correctamente la seguridad de null de Kotlin, mientras que Gson puede deserializar null en un campo no nulo, provocando una excepción.
@SerializedName vincula una clave JSON con un campo de clase cuando sus nombres no coinciden. Por ejemplo, para el campo kotlinName y la clave JSON "kotlin_name", la anotación @SerializedName("kotlin_name") garantiza una conversión correcta.
TypeToken es una clase abstracta que captura el parámetro de tipo a través de una clase anónima. Es necesaria para deserializar colecciones y otros tipos parametrizados, porque debido al borrado de tipos, Gson no puede recuperar el tipo de elemento en tiempo de ejecución.
Resumen
Desarrollaremos una aplicación móvil llave en mano
IT Sectr crea aplicaciones para iOS y Android para startups y empresas desde 2017. Le asesoraremos y le propondremos la mejor solución.
Lea también