web-dev-qa-db-fra.com

Gson Serialize field only if not null or not empty

J'ai une exigence où j'ai besoin de convertir Java objet en json.

J'utilise Gson pour cela, mais j'ai besoin du convertisseur pour sérialiser uniquement les valeurs non nulles ou non vides.

Par exemple:

//my Java object looks like
class TestObject{
    String test1;
    String test2;
    OtherObject otherObject = new OtherObject();
}

maintenant mon instance Gson pour convertir cet objet en json ressemble

Gson gson = new Gson();
TestObject obj = new TestObject();
obj.test1 = "test1";
obj.test2 = "";

String jsonStr = gson.toJson(obj);
println jsonStr;

Dans l'impression ci-dessus, le résultat est

{"test1":"test1", "test2":"", "otherObject":{}}

Ici, je voulais juste que le résultat soit

{"test1":"test1"}

Puisque le test2 est vide et otherObject est vide, je ne veux pas qu'ils soient sérialisés en données json.

Btw, j'utilise Groovy/Grails, donc s'il y a un plugin pour cela, ce serait bien, sinon une suggestion de personnaliser la classe de sérialisation gson serait bonne.

28
zdesam

Créez votre propre TypeAdapter

public class MyTypeAdapter extends TypeAdapter<TestObject>() {

    @Override
    public void write(JsonWriter out, TestObject value) throws IOException {
        out.beginObject();
        if (!Strings.isNullOrEmpty(value.test1)) {
            out.name("test1");
            out.value(value.test1);
        }

        if (!Strings.isNullOrEmpty(value.test2)) {
            out.name("test2");
            out.value(value.test1);
        }
        /* similar check for otherObject */         
        out.endObject();    
    }

    @Override
    public TestObject read(JsonReader in) throws IOException {
        // do something similar, but the other way around
    }
}

Vous pouvez ensuite l'enregistrer avec Gson.

Gson gson = new GsonBuilder().registerTypeAdapter(TestObject.class, new MyTypeAdapter()).create();
TestObject obj = new TestObject();
obj.test1 = "test1";
obj.test2 = "";
System.out.println(gson.toJson(obj));

produit

 {"test1":"test1"}

La classe GsonBuilder possède un tas de méthodes pour créer vos propres stratégies de sérialisation/désérialisation, enregistrer des adaptateurs de type et définir d'autres paramètres.

Strings est une classe de goyave. Vous pouvez faire votre propre vérification si vous ne voulez pas de cette dépendance.

21

Ce que je n'aime pas personnellement dans TypeAdapter en utilisant la réponse, c'est le fait que vous devez décrire chaque champ de votre classe entière qui pourrait avoir disons 50 champs (ce qui signifie 50 if blocs dans TypeAdapter).
Ma solution est basée sur Reflection et un fait Gson ne sérialisera pas les champs de valeurs nulles par défaut.
J'ai une classe spéciale qui contient des données pour l'API pour créer un document appelé DocumentModel, qui a environ 50 champs et je n'aime pas envoyer String champs avec "" (vide mais pas nul) valeurs ou tableaux vides sur le serveur. J'ai donc créé une méthode spéciale qui me renvoie une copie de mon objet avec tous les champs vides annulés. Remarque - par défaut, tous les tableaux de mon instance DocumentModel sont initialisés en tant que tableaux vides (longueur nulle) et ne sont donc jamais nuls, vous devriez probablement vérifier la nullité de vos tableaux avant de vérifier leur longueur.

public DocumentModel getSerializableCopy() {
    Field fields[] = new Field[]{};
    try {
        // returns the array of Field objects representing the public fields
        fields = DocumentModel.class.getDeclaredFields();
    } catch (Exception e) {
        e.printStackTrace();
    }
    DocumentModel copy = new DocumentModel();
    Object value;
    for (Field field : fields) {
        try {
            value = field.get(this);
            if (value instanceof String && TextUtils.isEmpty((String) value)) {
                field.set(copy, null);
            // note: here array is not being checked for null!
            else if (value instanceof Object[] && ((Object[]) value).length == 0) {
                field.set(copy, null);
            } else
                field.set(copy, value);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }
    return copy;
}

En utilisant cette méthode, je ne me soucie pas si certains champs ont été ajoutés ou supprimés après l'écriture de cette méthode ou autre chose. Le seul problème qui reste - vérifie les champs de type personnalisé, qui ne sont pas String ou tableau, mais cela dépend de la classe particulière et doit être codé en plus dans les blocs if/else.

7
Stan

Il me semble que le problème n'est pas avec gson. Gson conserve correctement la différence entre null et une chaîne vide. Voulez-vous vraiment effacer cette distinction? Êtes-vous sûr que toutes les classes qui utilisent TestObject s'en moquent?

Ce que vous pouvez faire si vous ne vous souciez pas de la différence, c'est de changer les chaînes vides en null dans un TestObject avant de le sérialiser. Ou mieux, définissez les setters dans TestObject de telle sorte qu'une chaîne vide soit définie sur null; de cette façon, vous définissez de manière rigide au sein de la classe qu'une chaîne vide est identique à null. Vous devrez vous assurer que les valeurs ne peuvent pas être définies en dehors des setters.

3
toto2