Reputation: 7953
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 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
}
and class B has got some private fiedls which are in class A
now when i try to create JSON string from above class A i get the following exception :
class com.hexgen.ro.request.A declares multiple JSON fields named netAmountPcy
How to fix this?
Since they are private fields there should not be any problem while creating json string i guess but i am not sure.
i create json string like the following :
Gson gson = new Gson();
tempJSON = gson.toJson(obj);
here obj is the object of class A
Upvotes: 91
Views: 129957
Reputation: 190
if you need subClass "override" superClass same name filed, you can use ExclusionStrategy
to remember same name super fileds and ignore them, after done the convert, need clear the thread local temp data.
the ExclusionStrategy
import com.google.gson.ExclusionStrategy;
import com.google.gson.FieldAttributes;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;
public class SuperClassFieldExcludeStrategy implements ExclusionStrategy {
ThreadLocal<Set<String>> ignored=ThreadLocal.withInitial(HashSet::new);
@Override
public boolean shouldSkipField(FieldAttributes f) {
Class<?> superClass = f.getDeclaringClass().getSuperclass();
while (superClass!=null && superClass!=Object.class){
List<Field> superSameFileds = Arrays.stream(superClass.getDeclaredFields()).filter(x -> x.getName().equals(f.getName())).collect(Collectors.toList());
String superClassName=superClass.getName();
ignored.get().addAll(superSameFileds.stream().map(x->superClassName+"."+x.getName()).collect(Collectors.toList()));
superClass=superClass.getSuperclass();
}
String name = f.getDeclaringClass().getName()+"."+f.getName();
return ignored.get().contains(name);
}
@Override
public boolean shouldSkipClass(Class<?> clazz) {
return false;
}
}
gson creat part
SuperClassFieldExcludeStrategy superClassFieldExcludeStrategy=new SuperClassFieldExcludeStrategy()
Gson gson = new GsonBuilder()
.addSerializationExclusionStrategy(superClassFieldExcludeStrategy)
.addDeserializationExclusionStrategy(superClassFieldExcludeStrategy)
.create();
convert part
try{
gson.toJson(new Object());
}finally {
superClassFieldExcludeStrategy.ignored.remove();
}
Upvotes: 0
Reputation: 24464
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.
Upvotes: 21
Reputation: 165
In kotlin adding the @Transient
annotation for the variable on the parent class did the trick for me on a sealed
class with open variables.
Upvotes: 8
Reputation: 6529
For Kotlin-er:
val fieldsToExclude = listOf("fieldToExclude", "otherFieldToExclude")
GsonBuilder()
.setExclusionStrategies(object : ExclusionStrategy {
override fun shouldSkipField(f: FieldAttributes?) = f?.let { fieldsToExclude.contains(it.name) } ?: false
override fun shouldSkipClass(clazz: Class<*>?) = false
})
.create()
Upvotes: 1
Reputation: 2066
I used GsonBuilder
and ExclusionStrategy
to avoid the redundant fields as below, it is simple and straight forward.
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();
Upvotes: 13
Reputation: 3415
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);
Upvotes: 2
Reputation: 1741
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.
Upvotes: 4
Reputation: 5472
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
}
}
}
Upvotes: 2
Reputation: 601
Add following lines at the bottom of proguard.config (if you are using proguard in project)
-keepclassmembers class * {
private <fields>;
}
Upvotes: 6
Reputation: 781
This is a bit late, but I ran into this exact same problem as well. The only thing was that I wasn't able to modify the superclass as that code wasn't mine. The way that I resolved this was by creating an exclusion strategy that skipped any field that had a field of the same name present in a superclass. Here is my code for that class:
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;
}
}
}
I then set the Serialization and Deserialization exclusion strategies in the builder as follows:
builder.addDeserializationExclusionStrategy(new SuperclassExclusionStrategy());
builder.addSerializationExclusionStrategy(new SuperclassExclusionStrategy());
Hopefully this helps someone!
Upvotes: 78
Reputation: 41133
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;
Upvotes: 93