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 ich das beheben?
Da es sich um private Felder handelt, sollte es beim Erstellen eines JSON-Strings kein Problem geben, aber ich bin mir nicht sicher.
Ich erstelle einen JSON-String wie folgt:
Gson gson = new Gson();
tempJSON = gson.toJson(obj);
hier ist obj das Objekt der Klasse A.
Antworten:
Da es sich um private Felder handelt, sollte es beim Erstellen eines JSON-Strings keine Probleme geben
Ich glaube nicht, dass diese Aussage wahr ist. GSON überprü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 Sie ein bestimmtes Feld nicht einschließen möchten, müssen Sie es mit einem
transient
Schlüsselwort markieren , z.private transient BigDecimal tradeFeesPcy;
quelle
@Expose
richtig funktioniert, müssen Sie festlegen@Expose(serialize = false)
und sicherstellen, dass SieexcludeFieldsWithoutExposeAnnotation()
IhrGsonBuilder
Objekt aufrufen .new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create().toJson(YOUR_OBJECT)
, es funktioniertkotlin /** * Marks the JVM backing field of the annotated property as `transient`, meaning that it is not * part of the default serialized form of the object. */ @Target(FIELD) @Retention(AnnotationRetention.SOURCE) @MustBeDocumented public actual annotation class Transient
Das ist etwas spät, aber ich bin auch auf genau das gleiche Problem gestoßen. Das einzige war, dass ich die Superklasse nicht ändern konnte, da dieser Code nicht meiner war. Ich habe dieses Problem gelöst, indem ich eine Ausschlussstrategie erstellt habe, bei der alle Felder übersprungen wurden, in denen ein gleichnamiges Feld in einer Oberklasse 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!
quelle
Die gleiche Fehlermeldung tritt auch auf, wenn Sie unterschiedliche Felder haben, diese jedoch gleich sind
@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 und ihre Vorfahren an und prüfen Sie, ob dies der Fall ist.
quelle
Fügen Sie die folgenden Zeilen am Ende von proguard.config hinzu (wenn Sie proguard im Projekt verwenden).
-keepclassmembers class * { private <fields>; }
quelle
In meinem Fall war ich dumm genug, einen Adapter bei 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);
quelle
Ich habe
GsonBuilder
und benutztExclusionStrategy
um die redundanten Felder wie unten zu vermeiden, ist es einfach und unkompliziert.Gson json = new GsonBuilder() .setExclusionStrategies(new ExclusionStrategy() { @Override public boolean shouldSkipField(FieldAttributes f) { if(f.getName().equals("netAmountPcy")){ return true; } return false; } @Override public boolean shouldSkipClass(Class<?> clazz) { return false; } }).create();
quelle
Lösung für Kotlin, wie bei 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 } } }
quelle
Ich denke nicht, dass Sie die Mitglieder vorübergehend machen sollten, dies könnte zu Fehlern führen, da Mitglieder, die Sie möglicherweise in Zukunft benötigen, möglicherweise ausgeblendet sind.
Ich habe dieses Problem gelöst, indem ich eine benutzerdefinierte Namensstrategie verwendet und den vollständigen Klassennamen an den Json angehängt habe. Der Nachteil ist, dass dies zu einem größeren Json führen würde und wenn Sie ihn für so etwas wie eine Rest-API benötigen, wäre es seltsam Clients, um die Felder so zu benennen, aber ich musste nur serialisieren, um auf die Festplatte auf 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 würde also der vollständige kanonische Name angehängt. Dies würde dazu führen, dass die untergeordnete Klasse einen anderen Namen als die übergeordnete Klasse hat. Beim Deserialisieren wird jedoch der Wert der untergeordneten Klasse verwendet.
quelle