web-dev-qa-db-fra.com

la classe A déclare plusieurs champs JSON

j'ai une classe A qui a quelques champs privés et la même classe étend une autre classe B qui a aussi des champs privés qui sont en classe A.

public class A extends B {
    private BigDecimal netAmountTcy;
    private BigDecimal netAmountPcy;   
    private BigDecimal priceTo;  
    private String segment;

    private BigDecimal taxAmountTcy;
    private BigDecimal taxAmountPcy;   
    private BigDecimal tradeFeesTcy;
    private BigDecimal tradeFeesPcy;

// getter and setter for the above fields

}

et la classe B a des domaines privés qui sont en classe A

maintenant, lorsque j'essaie de créer une chaîne JSON à partir de la classe A ci-dessus, j'obtiens l'exception suivante:

class com.hexgen.ro.request.A declares multiple JSON fields named netAmountPcy

Comment régler ceci?

Comme ce sont des champs privés, il ne devrait pas y avoir de problème pour créer une chaîne json, mais je ne suis pas sûr.

je crée une chaîne json comme suit:

Gson gson = new Gson();
 tempJSON = gson.toJson(obj);

ici obj est l'objet de la classe A

62
Java Questions

Puisqu'ils sont des champs privés, il ne devrait pas y avoir de problème lors de la création d'une chaîne json

Je ne pense pas que cette affirmation soit vraie, GSON lève les yeux vers les champs privés de l'objet lors de la sérialisation, ce qui signifie que tous les champs privés de la superclasse sont inclus. Lorsque vous avez des champs de même nom, une erreur est renvoyée.

Si vous ne souhaitez pas inclure un champ particulier, vous devez le marquer avec le mot clé transient, par exemple:

private transient BigDecimal tradeFeesPcy;
63
gerrytan

C'est un peu tard, mais j'ai rencontré exactement le même problème. La seule chose était que je n'ai pas pu modifier la super-classe, car ce code n'était pas le mien. J'ai résolu ce problème en créant une stratégie d'exclusion qui ignorait tous les champs contenant un champ du même nom dans une superclasse. Voici mon code pour cette classe:

public class SuperclassExclusionStrategy implements ExclusionStrategy
{
    public boolean shouldSkipClass(Class<?> arg0)
    {
        return false;
    }

    public boolean shouldSkipField(FieldAttributes fieldAttributes)
    {
        String fieldName = fieldAttributes.getName();
        Class<?> theClass = fieldAttributes.getDeclaringClass();

        return isFieldInSuperclass(theClass, fieldName);            
    }

    private boolean isFieldInSuperclass(Class<?> subclass, String fieldName)
    {
        Class<?> superclass = subclass.getSuperclass();
        Field field;

        while(superclass != null)
        {   
            field = getField(superclass, fieldName);

            if(field != null)
                return true;

            superclass = superclass.getSuperclass();
        }

        return false;
    }

    private Field getField(Class<?> theClass, String fieldName)
    {
        try
        {
            return theClass.getDeclaredField(fieldName);
        }
        catch(Exception e)
        {
            return null;
        }
    }
}

J'ai ensuite défini les stratégies d'exclusion de la sérialisation et de la désérialisation dans le générateur comme suit:

    builder.addDeserializationExclusionStrategy(new SuperclassExclusionStrategy());
    builder.addSerializationExclusionStrategy(new SuperclassExclusionStrategy());

Espérons que cela aide quelqu'un!

59
Adrian Lee

Le même message d'erreur se produit également si vous avez différents champs mais qu'ils ont le même @SerializedName.

@SerializedName("date_created")
private Date DateCreated;
@SerializedName("date_created")
private Integer matchTime;

En copiant/collant, vous pouvez simplement faire une telle erreur. Alors, regardez dans la classe et ses ancêtres et vérifiez cela.

9
Gangnus

Ajoutez les lignes suivantes au bas de proguard.config (si vous utilisez proguard dans project)

-keepclassmembers class * {
    private <fields>;    
}
7
Sujay

Solution pour Kotlin, comme suggéré par Adrian-Lee, vous devez modifier quelques vérifications nulles

class SuperclassExclusionStrategy : ExclusionStrategy {

    override fun shouldSkipClass(clazz: Class<*>?): Boolean {
        return false
    }

    override fun shouldSkipField(f: FieldAttributes?): Boolean {
        val fieldName = f?.name
        val theClass = f?.declaringClass

        return isFieldInSuperclass(theClass, fieldName)
    }

    private fun isFieldInSuperclass(subclass: Class<*>?, fieldName: String?): Boolean {
        var superclass: Class<*>? = subclass?.superclass
        var field: Field?

        while (superclass != null) {
            field = getField(superclass, fieldName)

            if (field != null)
                return true

            superclass = superclass.superclass
        }

        return false
    }

    private fun getField(theClass: Class<*>, fieldName: String?): Field? {
        return try {
            theClass.getDeclaredField(fieldName)
        } catch (e: Exception) {
            null
        }

    }
}
1
Rafael Ruiz Muñoz

Je ne pense pas que vous devriez rendre les membres transitoires, cela pourrait conduire à des erreurs car les membres dont vous pourriez avoir besoin à l'avenir pourraient être cachés.

La façon dont j'ai résolu ce problème consiste à utiliser une stratégie de dénomination personnalisée et à ajouter le nom complet de la classe au Json. L'inconvénient est que cela conduirait à un Json plus volumineux et si vous en aviez besoin pour quelque chose comme un Api Rest, ce serait bizarre. clients pour nommer les champs de cette façon, mais je n’avais besoin que de la sérialisation pour écrire sur le disque sous Android.

Voici donc une implémentation d’une stratégie de nommage personnalisée dans Kotlin

import com.google.gson.FieldNamingStrategy
import Java.lang.reflect.Field

  class GsonFieldNamingStrategy : FieldNamingStrategy {
     override fun translateName(field: Field?): String? {
        return "${field?.declaringClass?.canonicalName}.${field?.name}"
    }
}

Donc, pour tous les champs, le nom canonique complet serait ajouté, ce qui donnerait à la classe enfant un nom différent de celui de la classe parent, mais lors de la désérialisation, la valeur de la classe enfant serait utilisée.

0
oziomajnr

Dans mon cas, j'étais assez stupide pour enregistrer un adaptateur avec la classe X et essayer de sérialiser fromJson avec la classe Y:

        final GsonBuilder gsonBuilder = new GsonBuilder();
        gsonBuilder.registerTypeAdapter(Game.class, new TournamentSerializer());
        final Gson gson = gsonBuilder.create();

        createdTournament = gson.fromJson(jsonResponse.toString(), Tournament.class);
0
RominaV