class A declares multiple JSON fields

JavaJsonGson

Java Problem Overview


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

Java Solutions


Solution 1 - Java

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;

Solution 2 - Java

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!

Solution 3 - Java

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.

  1. You cannot have two fields with the same name.
  2. You cannot have two fields with the same serialized name.
  3. Types are irrelevant for these rules.

Solution 4 - Java

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();

Solution 5 - Java

Add following lines at the bottom of proguard.config (if you are using proguard in project)

-keepclassmembers class * {
    private <fields>;    
}

Solution 6 - Java

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

Solution 7 - Java

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
        }

    }
}

Solution 8 - Java

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.

Solution 9 - Java

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()

Solution 10 - Java

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.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJava QuestionsView Question on Stackoverflow
Solution 1 - JavagerrytanView Answer on Stackoverflow
Solution 2 - JavaAdrian LeeView Answer on Stackoverflow
Solution 3 - JavaGangnusView Answer on Stackoverflow
Solution 4 - JavaSaifView Answer on Stackoverflow
Solution 5 - JavaSujayView Answer on Stackoverflow
Solution 6 - JavaRominaVView Answer on Stackoverflow
Solution 7 - JavaRafael Ruiz MuñozView Answer on Stackoverflow
Solution 8 - JavaoziomajnrView Answer on Stackoverflow
Solution 9 - JavaBesnikView Answer on Stackoverflow
Solution 10 - JavaREM CodeView Answer on Stackoverflow