Ho un'app che utilizza Gson per serializzare / deserializzare le classi di dati e mantenere i dati tra viene eseguito.
Il mio codice utilizza @NonNull
annotazioni per molti campi / parametri / ritorni del metodo e una cosa che era Cosa succede se I (o un altro sviluppatore) aggiunge un nuovo campo con annotazione @NonNull
(o anche solo annota un campo esistente ) in una classe di dati e Gson lo imposta su null
perché il JSON è stato salvato in una versione precedente dell'app?
Bene, ho risposto a questa domanda oggi. Come dice la guida dell'utente e i miei test confermano:
- Questa implementazione gestisce i valori null correttamente
- Durante la serializzazione, un campo null viene saltato dall'output
- Durante la deserializzazione, una voce mancante in JSON comporta l'impostazione del campo corrispondente nell'oggetto su null
In effetti, se aggiungo un nuovo campo alla mia classe di dati, potrebbe essere inizializzato su null
.
Quindi ho cercato una soluzione. Ho scoperto che se aggiungo un costruttore privato no-args alla classe data, ci sono abbastanza informazioni per l'IDE per segnalare che il campo potrebbe effettivamente essere null
. Un esempio di questa implementazione è elencato di seguito.
public void testDataClass1() throws Exception {
int value1 = 23;
int value2 = 48;
final DataClass1Version1 inputClass = new DataClass1Version1(value1, value2);
final String dataClass1Json = getGson().toJson(inputClass);
final DataClass1Version2 outputClass = getGson().fromJson(dataClass1Json, DataClass1Version2.class);
assertEquals(outputClass.getValue1(), value1);
assertEquals(outputClass.getValue2(), value2);
assertNotNull("New @NonNull field shouldn't be null.", outputClass.getSomeNewValue());
}
DataClass1Version1.java:
public class DataClass1Version1 {
private int value1;
private int value2;
public DataClass1Version1(int value1, int value2) {
this.value1 = value1;
this.value2 = value2;
}
public int getValue1() {
return value1;
}
public void setValue1(int value1) {
this.value1 = value1;
}
public int getValue2() {
return value2;
}
public void setValue2(int value2) {
this.value2 = value2;
}
@Override
public String toString() {
return "DataClass1Version1{" +
"value1=" + value1 +
", value2=" + value2 +
'}';
}
}
DataClass1Version2.java:
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
public class DataClass1Version2 {
private int value1;
private int value2;
@Nullable
private String someNullableString;
//@NonNull
//private String someNewValue; //you'll get a warning for not initializing this value
@NonNull
private String someNewValue = ""; //if you don't initialize this field here, it could be null after json deserialization
private DataClass1Version2() {
//adding this constructor caused a warning when someNewValue didn't have an initializer
}
public DataClass1Version2(int value1, int value2, @Nullable String someNullableString, @NonNull String someNewValue) {
this.value1 = value1;
this.value2 = value2;
this.someNullableString = someNullableString;
this.someNewValue = someNewValue;
}
public int getValue1() {
return value1;
}
public void setValue1(int value1) {
this.value1 = value1;
}
public int getValue2() {
return value2;
}
public void setValue2(int value2) {
this.value2 = value2;
}
@Nullable
public String getSomeNullableString() {
return someNullableString;
}
public void setSomeNullableString(@Nullable String someNullableString) {
this.someNullableString = someNullableString;
}
@NonNull
public String getSomeNewValue() {
return someNewValue;
}
public void setSomeNewValue(@NonNull String someNewValue) {
this.someNewValue = someNewValue;
}
@Override
public String toString() {
return "DataClass1Version2{" +
"value1=" + value1 +
", value2=" + value2 +
", someNullableString='" + someNullableString + '\'' +
", someNewValue='" + someNewValue + '\'' +
'}';
}
}
Il problema con questa soluzione è che generalmente uso i campi finali nelle mie classi di dati e come tale, un costruttore no-args non funzionerà. Cosa dovrei fare?
Durante la stesura di questo, ho pensato di inizializzare solo alcuni valori predefiniti e quindi suppongo che poiché Gson usa il reflection, sovrascriverà quei valori predefiniti, anche se sono campi finali. Voglio ancora sapere cosa pensano gli altri. Questo aggiunge ulteriore peso alla deserializzazione; se ho un gruppo di ArrayList e creo nuove istanze nel costruttore no-args, le nuove istanze non verranno immediatamente sostituite con l'istanza generata da Gson?
Ecco DataClass2Version2 con i campi finali e gli inizializzatori predefiniti:
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
public class DataClass2Version2 {
private final int value1;
private final int value2;
@Nullable
private final String someNullableString;
@NonNull
private final String someNewValue;
private DataClass2Version2() {
value1 = 0;
value2 = 0;
someNullableString = null;
someNewValue = "";
}
public DataClass2Version2(int value1, int value2, @Nullable String someNullableString, @NonNull String someNewValue) {
this.value1 = value1;
this.value2 = value2;
this.someNullableString = someNullableString;
this.someNewValue = someNewValue;
}
public int getValue1() {
return value1;
}
public int getValue2() {
return value2;
}
@Nullable
public String getSomeNullableString() {
return someNullableString;
}
@NonNull
public String getSomeNewValue() {
return someNewValue;
}
@Override
public String toString() {
return "DataClass2Version2{" +
"value1=" + value1 +
", value2=" + value2 +
", someNullableString='" + someNullableString + '\'' +
", someNewValue='" + someNewValue + '\'' +
'}';
}
}