i have a class A which has some private fields and the same class extends another class B which also has some private fields which are in class A.
public cla
Since they are private fields there should not be any problem while creating json string
I don't think this statement is true, GSON looks up at the object's private fields when serializing, meaning all private fields of superclass are included, and when you have fields with same name it throws an error.
If there's any particular field you don't want to include you have to mark it with transient
keyword, eg:
private transient BigDecimal tradeFeesPcy;
In my case I was dumb enough to register an adapter with X class, and try to serialize fromJson with Y class:
final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Game.class, new TournamentSerializer());
final Gson gson = gsonBuilder.create();
createdTournament = gson.fromJson(jsonResponse.toString(), Tournament.class);
I don't think you should make the members transient, this might lead to errors because members that you might need in the future might be hidden.
How I solved this problem is to use a custom naming strategy and append the full class name to the Json, the downside of this is that it would lead to larger Json and if you need it for something like a Rest Api it would be weird for clients to name the fields that way, but I only needed to serialize to write to disk on android.
So here is an implementation of a custom naming strategy 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}"
}
}
So for all fields, the full canonical name would be appended, this would make the child class have a different name from the parent class, but when deserializing, the child class value would be used.
The same error message also happens if you have different fields, but they have the same @SerializedName
.
@SerializedName("date_created")
private Date DateCreated;
@SerializedName("date_created")
private Integer matchTime;
Doing copy/paste you can simply make such mistake. So, look into the the class and its ancestors and check for that.
Add following lines at the bottom of proguard.config (if you are using proguard in project)
-keepclassmembers class * {
private <fields>;
}
Solution for Kotlin, as suggested @Adrian-Lee, you have to tweak some Null Checks
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
}
}
}