Ricerca…


introduzione

Gson è una libreria Java che può essere utilizzata per convertire oggetti Java nella loro rappresentazione JSON. Gson considera entrambi questi obiettivi di design molto importanti.

Caratteristiche di Gson:

Fornire toJson() semplici toJson() e fromJson() per convertire oggetti Java in JSON e viceversa

Consenti agli oggetti non modificabili preesistenti di essere convertiti da e verso JSON

Ampio supporto di Java Generics

Supporta oggetti arbitrariamente complessi (con gerarchie di ereditarietà profonde e uso estensivo di tipi generici)

Sintassi

  • Excluder excluder ()
  • FieldNamingStrategy fieldNamingStrategy ()
  • <T> T fromJson (JsonElement json, Class <T> classOfT)
  • <T> T fromJson (JsonElement json, Type typeOfT)
  • <T> T fromJson (lettore JsonReader, tipo typeOfT)
  • <T> T fromJson (Reader json, Class <T> classOfT)
  • <T> T fromJson (Reader json, Type typeOfT)
  • <T> T fromJson (String json, Class <T> classOfT)
  • <T> T fromJson (String json, Type typeOfT)
  • <T> TypeAdapter <T> getAdapter (tipo di classe <T>)
  • <T> TypeAdapter <T> getAdapter (TypeToken <T> type)
  • <T> TypeAdapter <T> getDelegateAdapter (TypeAdapterFactory skipPast, TypeToken <T> type)
  • JsonReader newJsonReader (Lettore di lettori)
  • JsonWriter newJsonWriter (writer writer)
  • JsonElement toJsonTree (Object src)
  • JsonElement toJsonTree (Object src, Type typeOfSrc)
  • booleano 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, JsonWriter writer)
  • void toJson (JsonElement jsonElement, writer Appendable)
  • void toJson (JsonElement jsonElement, writer JsonWriter)
  • void toJson (Object src, Appendable writer)

Parsing JSON con Gson

L'esempio mostra l'analisi di un oggetto JSON utilizzando la libreria Gson di Google .

Parsing objects:

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 = "";
    }


}

Quindi, se è necessario eseguire l'analisi, utilizzare quanto segue:

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

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

Analizzare una lista:

Quando si recupera un elenco di oggetti JSON, spesso si vorrà analizzarli e convertirli in oggetti Java.

La stringa JSON che tenteremo di convertire è la seguente:

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

Questo particolare array JSON contiene tre oggetti. Nel nostro codice Java vorremmo mappare questi oggetti su oggetti Dog . Un oggetto Dog dovrebbe assomigliare a questo:

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

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

Per convertire l'array JSON in un Dog[] :

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

Conversione di un Dog[] in una stringa JSON:

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

Per convertire l'array JSON in un ArrayList<Dog> possiamo fare quanto segue:

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

Il tipo Type typeListOfDogs definisce come dovrebbe apparire un elenco di oggetti Dog . GSON può utilizzare questo oggetto di tipo per mappare l'array JSON ai valori corretti.

In alternativa, la conversione di un List<Dog> in un array JSON può essere eseguita in modo simile.

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

Analizzare la proprietà JSON per enumare con Gson

Se vuoi analizzare una stringa per enumare con Gson:

{"status": "open"}

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

Analizzare una lista con Gson

Metodo 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());

Ciò è utile per la maggior parte delle classi contenitore generiche, poiché non è possibile ottenere la classe di un tipo parametrizzato (ad esempio: non è possibile chiamare List<String>.class ).

Metodo 2

public class StringList extends ArrayList<String> { }

...

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

In alternativa, puoi sempre sottoclassi il tipo che desideri e poi passare in quella classe. Tuttavia, questa non è sempre una buona pratica, poiché ti restituirà un oggetto di tipo StringList ;

Serializzazione / deserializzazione JSON con AutoValue e Gson

Importa nel tuo file radice gradle

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

Importa nel file dell'app 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'

Crea oggetto con autovalore:

@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 il tuo convertitore Gson con GsonBuilder

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

Deserialize

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

serializzare

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

Usare Gson è un ottimo modo per semplificare il codice di serializzazione e deserializzazione usando oggetti POJO. L'effetto collaterale è che la riflessione è costosa in termini di prestazioni. Ecco perché l'utilizzo di AutoValue-Gson per generare CustomTypeAdapter eviterà questo costo di riflessione rimanendo molto semplice da aggiornare quando si verifica un cambio API.

Parsing JSON to Generic Class Object con Gson

Supponiamo di avere una stringa JSON:

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

Possiamo analizzare questa stringa JSON in un array String :

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

Ma se vogliamo analizzarlo in un oggetto List<String> , dobbiamo usare TypeToken .

Ecco il campione:

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

Supponiamo di avere due classi qui sotto:

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

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

e abbiamo una stringa JSON che dovrebbe essere analizzata su un oggetto Outer<Person> .

Questo esempio mostra come analizzare questa stringa JSON sull'oggetto classe generico correlato:

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

Se la stringa JSON deve essere analizzata su un oggetto Outer<List<Person>> :

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

Aggiunta di Gson al tuo progetto

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

Per utilizzare l'ultima versione di Gson

La riga sottostante compilerà l'ultima versione della libreria gson ogni volta che si compila, non è necessario modificare la versione.

Pro: puoi utilizzare le ultime funzionalità, la velocità e meno bug.
Contro: Potrebbe rompere la compatibilità con il tuo codice.

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

Usare Gson per caricare un file JSON dal disco.

Questo caricherà un file JSON dal disco e lo convertirà nel tipo specificato.

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);
}

Aggiunta di un convertitore personalizzato a Gson

A volte è necessario serializzare o deserializzare alcuni campi in un formato desiderato, ad esempio il proprio back-end può utilizzare il formato "AAAA-MM-gg HH: mm" per le date e si desidera che il POJOS utilizzi la classe DateTime in Joda Time.

Per convertire automaticamente queste stringhe nell'oggetto DateTimes, è possibile utilizzare un convertitore personalizzato.

/**
 * 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());
    }
}

Per fare in modo che Gson usi il convertitore appena creato, devi assegnarlo durante la creazione dell'oggetto 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

Per deserializzare la data in quel formato devi solo definire un campo nel formato DateTime:

public class SomePojo {
    private DateTime someDate;    
} 

Quando Gson incontra un campo di tipo DateTime, chiamerà il tuo convertitore per deserializzare il campo.

Utilizzo di Gson come serializzatore con Retrofit

Prima di tutto è necessario aggiungere GsonConverterFactory al file build.gradle

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

Quindi, è necessario aggiungere la fabbrica del convertitore durante la creazione del servizio di retrofit:

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

È possibile aggiungere convertitori personalizzati durante la creazione dell'oggetto Gson che si sta passando alla fabbrica. Ti consente di creare conversioni di tipo personalizzato.

Analizzando l'array json in una classe generica usando Gson

Supponiamo di avere 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"
    }
  ]
}

Possiamo analizzare manualmente questo array in un oggetto Tweets personalizzato (contenitore dell'elenco dei tweet), ma è più facile farlo con il metodo fromJson :

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

Supponiamo di avere due classi qui sotto:

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;
}

e se hai solo bisogno di analizzare un array json puoi usare questo codice nel tuo parsing:

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

Deserializzatore JSON personalizzato usando Gson

Immagina di avere tutte le date in tutte le risposte in qualche formato personalizzato, ad esempio /Date(1465935152)/ e vuoi applicare la regola generale per deserializzare tutte le date di Json alle istanze di Date java. In questo caso è necessario implementare JS Deserializer personalizzato.

Esempio di JSON:

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

Supponiamo di avere questa classe qui sotto:

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

Deserializzatore personalizzato:

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());
    }
}

E l'uso:

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

Serializza e deserializza le stringhe di Jackson JSON con i tipi di data

Questo vale anche nel caso in cui si desideri rendere compatibile la conversione di Gson Date con Jackson, ad esempio.

Jackson solitamente serializza Data in "millisecondi dall'epoca", mentre Gson utilizza un formato leggibile come Aug 31, 2016 10:26:17 per rappresentare Data. Ciò porta a JsonSyntaxExceptions in Gson quando si tenta di deserializzare una data in formato Jackson.

Per aggirare questo problema, puoi aggiungere un serializzatore personalizzato e un deserializzatore personalizzato:

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();

Usare Gson con ereditarietà

Gson non supporta l'ereditarietà fuori dalla scatola.

Diciamo che abbiamo la seguente gerarchia di classi:

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;
    }
}

E ora vogliamo serializzare un'istanza di DerivedClass1 in una stringa JSON

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

Ora, in un altro posto, riceviamo questa stringa json e vogliamo deserializzarla - ma in fase di compilazione sappiamo solo che si suppone che sia un'istanza di BaseClass :

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

Ma GSON non sa che derivedClass1Json era in origine un'istanza di DerivedClass1 , quindi verrà stampato 10.

Come risolvere questo?

È necessario creare il proprio JsonDeserializer , che gestisce tali casi. La soluzione non è perfettamente pulita, ma non ho potuto trovare una soluzione migliore.

Innanzitutto, aggiungi il seguente campo alla tua classe base

@SerializedName("type")
private String typeName;

E inizializzalo nel costruttore della classe base

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

Ora aggiungi la seguente classe:

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);
 }
}

Tutto quello che resta da fare è agganciare tutto -

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

E ora, eseguendo il seguente codice-

 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());

Stamperà 5.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow