класс 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}"
}
}
Таким образом, для всех полей будет добавлено полное каноническое имя, что приведет к тому, что дочерний класс будет иметь имя, отличное от родительского класса, но при десериализации будет использоваться значение дочернего класса.