Buscar..


Introducción

Gson es una biblioteca de Java que se puede usar para convertir objetos de Java en su representación JSON. Gson considera que ambos son objetivos de diseño muy importantes.

Características de Gson:

Proporcione toJson() simples toJson() y fromJson() para convertir objetos Java a JSON y viceversa

Permitir que los objetos no modificables preexistentes se conviertan ay desde JSON

Amplio soporte de Java Generics

Admite objetos complejos arbitrariamente (con jerarquías de herencia profundas y uso extensivo de tipos genéricos)

Sintaxis

  • Excluder excluder ()
  • FieldNamingStrategy fieldNamingStrategy ()
  • <T> T fromJson (JsonElement json, Class <T> classOfT)
  • <T> T fromJson (JsonElement json, tipo typeOfT)
  • <T> T fromJson (lector JsonReader, tipo typeOfT)
  • <T> T fromJson (Reader json, Class <T> classOfT)
  • <T> T fromJson (Reader json, tipo typeOfT)
  • <T> T fromJson (String json, Class <T> classOfT)
  • <T> T fromJson (String json, Type typeOfT)
  • <T> TypeAdapter <T> getAdapter (clase <T> tipo)
  • <T> TypeAdapter <T> getAdapter (TypeToken <T> type)
  • <T> TypeAdapter <T> getDelegateAdapter (TypeAdapterFactory skipPast, TypeToken <T> type)
  • JsonReader newJsonReader (lector de lectores)
  • JsonWriter newJsonWriter (escritor escritor)
  • JsonElement toJsonTree (Object src)
  • JsonElement toJsonTree (Object src, Type typeOfSrc)
  • boolean serializeNulls ()
  • booleano htmlSafe ()
  • String toJson (JsonElement jsonElement)
  • String toJson (Object src)
  • String toJson (Object src, Type typeOfSrc)
  • String toString ()
  • void toJson (Object src, Type typeOfSrc, Appendable writer)
  • void toJson (Object src, Type typeOfSrc, escritor JsonWriter)
  • void toJson (JsonElement jsonElement, escritor anexable)
  • void toJson (JsonElement jsonElement, escritor JsonWriter)
  • void toJson (Object src, Appendable writer)

Analizando JSON con Gson

El ejemplo muestra el análisis de un objeto JSON utilizando la biblioteca Gson de Google .

Analizando objetos:

class Robot {
    //OPTIONAL - this annotation allows for the key to be different from the field name, and can be omitted if key and field name are same . Also this is good coding practice as it decouple your variable names with server keys name 
    @SerializedName("version") 
    private String version;

    @SerializedName("age")
    private int age;
    
    @SerializedName("robotName")
    private String name;
    
    // optional : Benefit it allows to set default values and retain them, even if key is missing from Json response. Not required for primitive data types. 

    public Robot{
       version = "";
       name = "";
    }


}

Luego, cuando sea necesario realizar un análisis, use lo siguiente:

String robotJson = "{
                        \"version\": \"JellyBean\",
                        \"age\": 3,
                        \"robotName\": \"Droid\"
                   }";

Gson gson = new Gson();
Robot robot = gson.fromJson(robotJson, Robot.class);

Analizando una lista:

Al recuperar una lista de objetos JSON, a menudo querrá analizarlos y convertirlos en objetos Java.

La cadena JSON que intentaremos convertir es la siguiente:

{
  "owned_dogs": [
    {
      "name": "Ron",
      "age": 12,
      "breed": "terrier"
    },
    {
      "name": "Bob",
      "age": 4,
      "breed": "bulldog"
    },
    {
      "name": "Johny",
      "age": 3,
      "breed": "golden retriever"
    }
  ]
}

Esta matriz JSON particular contiene tres objetos. En nuestro código Java, querremos asignar estos objetos a objetos Dog . Un objeto de perro se vería así:

private class Dog {
    public String name;
    public int age;

    @SerializedName("breed")
    public String breedName;
}

Para convertir la matriz JSON en un Dog[] :

Dog[] arrayOfDogs = gson.fromJson(jsonArrayString, Dog[].class);

Convertir un Dog[] en una cadena JSON:

String jsonArray = gson.toJson(arrayOfDogs, Dog[].class);

Para convertir la matriz JSON en un ArrayList<Dog> podemos hacer lo siguiente:

Type typeListOfDogs = new TypeToken<List<Dog>>(){}.getType();
List<Dog> listOfDogs = gson.fromJson(jsonArrayString, typeListOfDogs);

El objeto Type typeListOfDogs define el aspecto que tendría una lista de objetos Dog . GSON puede usar este tipo de objeto para asignar la matriz JSON a los valores correctos.

Alternativamente, la conversión de una List<Dog> a una matriz JSON se puede hacer de una manera similar.

String jsonArray = gson.toJson(listOfDogs, typeListOfDogs);

Analizar la propiedad JSON para enumerar con Gson

Si desea analizar una cadena para enumerar con Gson:

{"estado": "abierto"}

public enum Status {
    @SerializedName("open")
    OPEN,
    @SerializedName("waiting")
    WAITING,
    @SerializedName("confirm")
    CONFIRM,
    @SerializedName("ready")
    READY
}

Analizar una lista con Gson

Método 1

Gson gson = new Gson();
String json = "[ \"Adam\", \"John\", \"Mary\" ]";

Type type = new TypeToken<List<String>>(){}.getType();
List<String> members = gson.fromJson(json, type);
Log.v("Members", members.toString());

Esto es útil para la mayoría de las clases de contenedores genéricos, ya que no puede obtener la clase de un tipo parametrizado (es decir, no puede llamar a la List<String>.class ).

Método 2

public class StringList extends ArrayList<String> { }

...

List<String> members = gson.fromJson(json, StringList.class);

Alternativamente, siempre puede subclasificar el tipo que desee y luego pasar esa clase. Sin embargo, esto no siempre es la mejor práctica, ya que le devolverá un objeto de tipo StringList ;

Serialización / Deserialización JSON con AutoValue y Gson

Importa en tu archivo root de gradle

classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'

Importa en tu aplicación de Gradle

apt 'com.google.auto.value:auto-value:1.2'  
apt 'com.ryanharter.auto.value:auto-value-gson:0.3.1'  
provided 'com.jakewharton.auto.value:auto-value-annotations:1.2-update1'  
provided 'org.glassfish:javax.annotation:10.0-b28'

Crear objeto con autovalue:

@AutoValue public abstract class SignIn {    
    @SerializedName("signin_token") public abstract String signinToken();
    public abstract String username();

    public static TypeAdapter<SignIn> typeAdapter(Gson gson) {
        return new AutoValue_SignIn.GsonTypeAdapter(gson);
    }

    public static SignIn create(String signin, String username) {
        return new AutoValue_SignIn(signin, username);
    }
}

Crea tu convertidor Gson con tu GsonBuilder

Gson gson = new GsonBuilder()
                .registerTypeAdapterFactory(
                    new AutoValueGsonTypeAdapterFactory())
                .create());

Deserializar

String myJsonData = "{
    \"signin_token\": \"mySigninToken\",
    \"username\": \"myUsername\" }";
SignIn signInData = gson.fromJson(myJsonData, Signin.class);

Publicar por fascículos

Signin myData = SignIn.create("myTokenData", "myUsername");
String myJsonData = gson.toJson(myData);

Usar Gson es una excelente manera de simplificar el código de serialización y deserialización utilizando objetos POJO. El efecto secundario es que la reflexión es costosa en términos de rendimiento. Es por eso que el uso de AutoValue-Gson para generar CustomTypeAdapter evitará este costo de reflexión y se mantendrá muy simple de actualizar cuando ocurra un cambio de API.

Análisis de JSON a objetos de clase genéricos con Gson

Supongamos que tenemos una cadena JSON:

["first","second","third"]

Podemos analizar esta cadena JSON en una matriz de String :

Gson gson = new Gson();
String jsonArray = "[\"first\",\"second\",\"third\"]";
String[] strings = gson.fromJson(jsonArray, String[].class);

Pero si queremos analizarlo en un objeto List<String> , debemos usar TypeToken .

Aquí está la muestra:

Gson gson = new Gson();
String jsonArray = "[\"first\",\"second\",\"third\"]";
List<String> stringList = gson.fromJson(jsonArray, new TypeToken<List<String>>() {}.getType());

Supongamos que tenemos dos clases a continuación:

public class Outer<T> {
    public int index;
    public T data;
}

public class Person {
    public String firstName;
    public String lastName;
}

y tenemos una cadena JSON que debe analizarse a un objeto Outer<Person> .

Este ejemplo muestra cómo analizar esta cadena JSON al objeto de clase genérico relacionado:

String json = "......";
Type userType = new TypeToken<Outer<Person>>(){}.getType();
Result<User> userResult = gson.fromJson(json,userType);

Si la cadena JSON se debe analizar a un objeto Outer<List<Person>> :

Type userListType = new TypeToken<Outer<List<Person>>>(){}.getType();
Result<List<User>> userListResult = gson.fromJson(json,userListType); 

Añadiendo Gson a tu proyecto

dependencies {
    compile 'com.google.code.gson:gson:2.8.1'
}

Para usar la última versión de Gson

La siguiente línea compilará la última versión de gson library cada vez que compile, no tiene que cambiar la versión.

Pros: Puedes usar las últimas funciones, velocidad y menos errores.
Contras: Podría romper la compatibilidad con tu código.

compile 'com.google.code.gson:gson:+'

Usando Gson para cargar un archivo JSON desde el disco.

Esto cargará un archivo JSON desde el disco y lo convertirá al tipo dado.

public static <T> T getFile(String fileName, Class<T> type) throws FileNotFoundException {
    Gson gson = new GsonBuilder()
            .create();
    FileReader json = new FileReader(fileName);
    return gson.fromJson(json, type);
}

Agregar un convertidor personalizado a Gson

A veces necesita serializar o deserializar algunos campos en un formato deseado, por ejemplo, su backend puede usar el formato "YYYY-MM-dd HH: mm" para las fechas y desea que su POJOS use la clase DateTime en Joda Time.

Para convertir automáticamente estas cadenas en el objeto DateTimes, puede usar un convertidor personalizado.

/**
 * Gson serialiser/deserialiser for converting Joda {@link DateTime} objects.
 */
public class DateTimeConverter implements JsonSerializer<DateTime>, JsonDeserializer<DateTime> {

    private final DateTimeFormatter dateTimeFormatter;

    @Inject
    public DateTimeConverter() {
        this.dateTimeFormatter = DateTimeFormat.forPattern("YYYY-MM-dd HH:mm");
    }

    @Override
    public JsonElement serialize(DateTime src, Type typeOfSrc, JsonSerializationContext context) {
        return new JsonPrimitive(dateTimeFormatter.print(src));
    }

    @Override
    public DateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context)
            throws JsonParseException {
        
        if (json.getAsString() == null || json.getAsString().isEmpty()) {
            return null;
        }

        return dateTimeFormatter.parseDateTime(json.getAsString());
    }
}

Para hacer que Gson use el convertidor recién creado, debe asignarlo al crear el objeto Gson:

    DateTimeConverter dateTimeConverter = new DateTimeConverter();
    Gson gson = new GsonBuilder().registerTypeAdapter(DateTime.class, dateTimeConverter)
            .create();

    String s = gson.toJson(DateTime.now());
    // this will show the date in the desired format

Para deserializar la fecha en ese formato, solo tiene que definir un campo en el formato DateTime:

public class SomePojo {
    private DateTime someDate;    
} 

Cuando Gson encuentra un campo de tipo DateTime, llamará a su convertidor para deserializar el campo.

Usando Gson como serializador con Retrofit

En primer lugar, debe agregar GsonConverterFactory a su archivo build.gradle

compile 'com.squareup.retrofit2:converter-gson:2.1.0'

Luego, debe agregar la fábrica de convertidores al crear el Servicio de actualización:

Gson gson = new GsonBuilder().create();
new Retrofit.Builder()
        .baseUrl(someUrl)
        .addConverterFactory(GsonConverterFactory.create(gson))
        .build()
        .create(RetrofitService.class);

Puede agregar convertidores personalizados al crear el objeto Gson que está pasando a la fábrica. Le permite crear conversiones de tipo personalizado.

Analizar la matriz json a una clase genérica usando Gson

Supongamos que tenemos un json:

{
  "total_count": 132,
  "page_size": 2,
  "page_index": 1,
  "twitter_posts": [
    {
      "created_on": 1465935152,
      "tweet_id": 210462857140252672,
      "tweet": "Along with our new #Twitterbird, we've also updated our Display Guidelines",
      "url": "https://twitter.com/twitterapi/status/210462857140252672"
    },
    {
      "created_on": 1465995741,
      "tweet_id": 735128881808691200,
      "tweet": "Information on the upcoming changes to Tweets is now on the developer site",
      "url": "https://twitter.com/twitterapi/status/735128881808691200"
    }
  ]
}

Podemos analizar esta matriz en un objeto de Tweets personalizados (contenedor de listas de tweets) manualmente, pero es más fácil hacerlo con el método fromJson :

Gson gson = new Gson();
String jsonArray = "....";
Tweets tweets = gson.fromJson(jsonArray, Tweets.class);

Supongamos que tenemos dos clases a continuación:

class Tweets {
    @SerializedName("total_count")
    int totalCount;
    @SerializedName("page_size")
    int pageSize;
    @SerializedName("page_index")
    int pageIndex;
    // all you need to do it is just define List variable with correct name 
    @SerializedName("twitter_posts")
    List<Tweet> tweets;
}

class Tweet {
    @SerializedName("created_on")
    long createdOn;
    @SerializedName("tweet_id")
    String tweetId;
    @SerializedName("tweet")
    String tweetBody;
    @SerializedName("url")
    String url;
}

y si solo necesita analizar una matriz json, puede usar este código en su análisis:

String tweetsJsonArray = "[{.....},{.....}]"
List<Tweet> tweets = gson.fromJson(tweetsJsonArray, new TypeToken<List<Tweet>>() {}.getType());

Deserializador JSON personalizado utilizando Gson

Imagine que tiene todas las fechas en todas las respuestas en algún formato personalizado, por ejemplo /Date(1465935152)/ y desea aplicar la regla general para deserializar todas las fechas Json a las instancias de Date Java. En este caso, debe implementar Json Deserializer personalizado.

Ejemplo de json:

{
  "id": 1,
  "created_on": "Date(1465935152)",
  "updated_on": "Date(1465968945)",
  "name": "Oleksandr"
}

Supongamos que tenemos esta clase a continuación:

class User {
    @SerializedName("id")
    long id;
    @SerializedName("created_on")
    Date createdOn;
    @SerializedName("updated_on")
    Date updatedOn;
    @SerializedName("name")
    String name;
}

Deserializador personalizado:

class DateDeSerializer implements JsonDeserializer<Date> {
    private static final String DATE_PREFIX = "/Date(";
    private static final String DATE_SUFFIX = ")/";

    @Override
    public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        String dateString = json.getAsString();
        if (dateString.startsWith(DATE_PREFIX) && dateString.endsWith(DATE_SUFFIX)) {
            dateString = dateString.substring(DATE_PREFIX.length(), dateString.length() - DATE_SUFFIX.length());
        } else {
            throw new JsonParseException("Wrong date format: " + dateString);
        }
        return new Date(Long.parseLong(dateString) - TimeZone.getDefault().getRawOffset());
    }
}

Y el uso:

Gson gson = new GsonBuilder()
                .registerTypeAdapter(Date.class, new DateDeSerializer())
                .create();
String json = "....";
User user = gson.fromJson(json, User.class);

Serializar y deserializar cadenas Jackson JSON con tipos de fecha

Esto también se aplica al caso en el que desea que la conversión de Gson Date sea compatible con Jackson, por ejemplo.

Jackson usualmente serializa Date a "milisegundos desde la época", mientras que Gson usa un formato legible como el Aug 31, 2016 10:26:17 para representar la fecha. Esto lleva a JsonSyntaxExceptions en Gson cuando intenta deserializar una fecha de formato Jackson.

Para evitar esto, puede agregar un serializador personalizado y un deserializador personalizado:

JsonSerializer<Date> ser = new JsonSerializer<Date>() {
    @Override
    public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext
                context) {
        return src == null ? null : new JsonPrimitive(src.getTime());
    }
};

JsonDeserializer<Date> deser = new JsonDeserializer<Date>() {
    @Override
    public Date deserialize(JsonElement json, Type typeOfT,
                JsonDeserializationContext context) throws JsonParseException {
        return json == null ? null : new Date(json.getAsLong());
    }
};

Gson gson = new GsonBuilder()
                .registerTypeAdapter(Date.class, ser)
                .registerTypeAdapter(Date.class, deser)
                .create();

Usando Gson con herencia

Gson no admite herencia fuera de la caja.

Digamos que tenemos la siguiente jerarquía de clases:

public class BaseClass {
    int a;
 
    public int getInt() {
        return a;
   }
}
 
public class DerivedClass1 extends BaseClass {
     int b;
 
     @Override
     public int getInt() {
         return b;
     }
 }
 
public class DerivedClass2 extends BaseClass {
    int c;
 
    @Override
    public int getInt() {
        return c;
    }
}

Y ahora queremos serializar una instancia de DerivedClass1 a una cadena JSON

DerivedClass1 derivedClass1 = new DerivedClass1();
derivedClass1.b = 5;
derivedClass1.a = 10;
 
Gson gson = new Gson();
String derivedClass1Json = gson.toJson(derivedClass1);

Ahora, en otro lugar, recibimos esta cadena json y queremos deserializarla, pero en tiempo de compilación solo sabemos que se supone que es una instancia de BaseClass :

BaseClass maybeDerivedClass1 = gson.fromJson(derivedClass1Json, BaseClass.class);
System.out.println(maybeDerivedClass1.getInt());

Pero GSON no sabe que derivedClass1Json fue originalmente una instancia de DerivedClass1 , por lo que esto imprimirá 10.

¿Cómo resolver esto?

Necesita crear su propio JsonDeserializer , que maneja tales casos. La solución no está perfectamente limpia, pero no pude encontrar una mejor.

Primero, agregue el siguiente campo a su clase base

@SerializedName("type")
private String typeName;

E inicialízalo en el constructor de la clase base.

public BaseClass() {
    typeName = getClass().getName();
}

Ahora agregue la siguiente clase:

public class JsonDeserializerWithInheritance<T> implements JsonDeserializer<T> {
 
 @Override
 public T deserialize(
     JsonElement json, Type typeOfT, JsonDeserializationContext context)
     throws JsonParseException {
     JsonObject jsonObject = json.getAsJsonObject();
     JsonPrimitive classNamePrimitive = (JsonPrimitive) jsonObject.get("type");
 
     String className = classNamePrimitive.getAsString();
 
     Class<?> clazz;
     try {
     clazz = Class.forName(className);
     } catch (ClassNotFoundException e) {
     throw new JsonParseException(e.getMessage());
     }
     return context.deserialize(jsonObject, clazz);
 }
}

Todo lo que queda por hacer es conectar todo

GsonBuilder builder = new GsonBuilder();
 builder
 .registerTypeAdapter(BaseClass.class, new JsonDeserializerWithInheritance<BaseClass>());
 Gson gson = builder.create();

Y ahora, ejecutando el siguiente código-

 DerivedClass1 derivedClass1 = new DerivedClass1();
 derivedClass1.b = 5;
 derivedClass1.a = 10;
 String derivedClass1Json = gson.toJson(derivedClass1);
 
 BaseClass maybeDerivedClass1 = gson.fromJson(derivedClass1Json, BaseClass.class);
 System.out.println(maybeDerivedClass1.getInt());

Se imprimirá 5.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow