класс A объявляет несколько полей JSON

У меня есть класс A, который имеет некоторые частные поля, и тот же класс расширяет другой класс B, который также имеет некоторые частные поля, которые находятся в классе 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

}

и класс B имеет несколько частных фидзелов, которые находятся в классе A

теперь, когда я пытаюсь создать строку JSON из класса A, я получаю следующее исключение:

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

Как это исправить?

Поскольку они являются частными полями, не должно возникнуть никаких проблем при создании строки json, я думаю, но я не уверен.

Я создаю строку json следующим образом:

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

здесь obj - объект класса A

Ответ 1

Поскольку они являются частными полями, не возникает никаких проблем при создании строки json

Я не думаю, что это утверждение верно, GSON смотрит на закрытые поля объекта при сериализации, то есть все частные поля суперкласса включены, и когда у вас есть поля с тем же именем, это вызывает ошибку.

Если есть какое-либо конкретное поле, которое вы не хотите включать, вы должны пометить его ключевым словом transient, например:

private transient BigDecimal tradeFeesPcy;

Ответ 2

Это немного поздно, но я столкнулся с этой же проблемой. Единственное, что я не смог изменить надкласс, поскольку этот код не был моим. Способ, которым я решил это, заключался в создании стратегии исключения, которая пропускала любое поле, в котором было поле с таким же именем, что и в суперклассе. Вот мой код для этого класса:

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

Затем я устанавливаю стратегии исключения сериализации и десериализации в построителе следующим образом:

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

Надеюсь, это поможет кому-то!

Ответ 3

Такое же сообщение об ошибке также происходит, если у вас разные поля, но они имеют одинаковое имя @SerializedName.

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

Выполняя копирование/вставку, вы можете просто сделать такую ошибку. Итак, загляните в класс и его предков и проверьте это.

Ответ 4

Добавьте следующие строки внизу proguard.config (если вы используете proguard в проекте)

-keepclassmembers class * {
    private <fields>;    
}

Ответ 5

Решение для Kotlin, как предложил @Адриан-Ли, вам нужно настроить некоторые 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
        }

    }
}

Ответ 6

В моем случае я был достаточно глуп, чтобы зарегистрировать адаптер с классом X и попытаться сериализовать из Json с классом Y:

final GsonBuilder gsonBuilder = new GsonBuilder();
gsonBuilder.registerTypeAdapter(Game.class, new TournamentSerializer());
final Gson gson = gsonBuilder.create();

createdTournament = gson.fromJson(jsonResponse.toString(), Tournament.class);

Ответ 7

Я не думаю, что вы должны делать переходные элементы, это может привести к ошибкам, потому что члены, которые вам могут понадобиться в будущем, могут быть скрыты.

Я решил эту проблему, используя собственную стратегию именования и добавив полное имя класса в Json, недостатком этого является то, что это приведет к увеличению Json, и если вам понадобится что-то вроде Rest Api, это будет странно для клиенты называют поля таким образом, но мне нужно было только сериализовать, чтобы записать на диск на Android.

Итак, вот реализация пользовательской стратегии именования в Котлине

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

Таким образом, для всех полей будет добавлено полное каноническое имя, что приведет к тому, что дочерний класс будет иметь имя, отличное от родительского класса, но при десериализации будет использоваться значение дочернего класса.