it-swarm.com.de

Klasse A deklariert mehrere JSON-Felder

ich habe eine Klasse A, die einige private Felder hat und die gleiche Klasse erweitert eine andere Klasse B, die auch einige private Felder hat, die in Klasse A sind.

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

}

und Klasse B hat einige private Fiedls, die in Klasse A sind

wenn ich jetzt versuche, eine JSON-Zeichenfolge aus der obigen Klasse A zu erstellen, erhalte ich die folgende Ausnahme:

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

Wie kann man das beheben?

Da es sich um private Felder handelt, sollte es beim Erstellen eines JSON-Strings keine Probleme geben, aber ich bin mir nicht sicher.

ich erstelle JSON-Zeichenfolge wie folgt:

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

hier ist obj das Objekt der Klasse A

62
Java Questions

Da es sich um private Felder handelt, sollte es beim Erstellen eines JSON-Strings keine Probleme geben.

Ich glaube nicht, dass diese Aussage zutrifft. GSON prüft beim Serialisieren die privaten Felder des Objekts, was bedeutet, dass alle privaten Felder der Oberklasse enthalten sind. Wenn Sie Felder mit demselben Namen haben, wird ein Fehler ausgegeben.

Wenn es ein bestimmtes Feld gibt, das Sie nicht einschließen möchten, müssen Sie es mit dem Schlüsselwort transient markieren, z.

private transient BigDecimal tradeFeesPcy;
63
gerrytan

Das ist ein bisschen spät, aber ich bin auch auf genau dasselbe Problem gestoßen. Das einzige war, dass ich die Superklasse nicht ändern konnte, da dieser Code nicht von mir stammte. Die Lösung bestand darin, eine Ausschlussstrategie zu erstellen, bei der alle Felder übersprungen wurden, bei denen in einer Superklasse ein Feld mit demselben Namen vorhanden war. Hier ist mein Code für diese Klasse:

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

Anschließend habe ich die Ausschlussstrategien für Serialisierung und Deserialisierung im Builder wie folgt festgelegt:

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

Hoffentlich hilft das jemandem!

59
Adrian Lee

Dieselbe Fehlermeldung tritt auch auf, wenn Sie unterschiedliche Felder haben, diese jedoch das gleiche @SerializedName.

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

Beim Kopieren/Einfügen können Sie einfach einen solchen Fehler machen. Schauen Sie sich also die Klasse nd ihre Vorfahren an und prüfen Sie, ob dies der Fall ist.

9
Gangnus

Fügen Sie die folgenden Zeilen am Ende von proguard.config hinzu (wenn Sie proguard in project verwenden

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

Lösung für Kotlin, wie @ Adrian-Lee vorgeschlagen, müssen Sie einige Null-Checks optimieren

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

Ich denke nicht, dass Sie die Mitglieder vorübergehend machen sollten. Dies kann zu Fehlern führen, da Mitglieder, die Sie in Zukunft benötigen, möglicherweise ausgeblendet werden.

Wie ich dieses Problem gelöst habe, ist es, eine benutzerdefinierte Namensstrategie zu verwenden und den vollständigen Klassennamen an den Json anzuhängen. Der Nachteil davon ist, dass dies zu einem größeren Json führen würde und wenn Sie es für so etwas wie eine Rest-API benötigen, wäre es seltsam für Clients, um die Felder so zu benennen, aber ich musste nur serialisieren, um auf die Festplatte unter Android zu schreiben.

Hier ist eine Implementierung einer benutzerdefinierten Namensstrategie in 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}"
    }
}

Für alle Felder wird der vollständige kanonische Name angehängt, wodurch die untergeordnete Klasse einen anderen Namen als die übergeordnete Klasse erhält. Beim Deserialisieren wird jedoch der Wert der untergeordneten Klasse verwendet.

0
oziomajnr

In meinem Fall war ich dumm genug, einen Adapter mit der X-Klasse zu registrieren und zu versuchen, von Json mit der Y-Klasse zu serialisieren:

        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