类A声明多个JSON字段


75

我有一个类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字符串时,出现以下异常:

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
发布您的超级B类
。– NPKR

Answers:


79

由于它们是私有字段,因此在创建json字符串时应该没有任何问题

我认为这句话是不对的,GSON在序列化时会查找对象的私有字段,这意味着包括了超类的所有私有字段,而当您使用相同名称的字段时,它将引发错误。

如果您不想包含任何特定字段,则必须用transient关键字标记它,例如:

private transient BigDecimal tradeFeesPcy;

谢谢你的工作。但是,为什么Gson的@Expose注释不能与supreclass字段一起使用,却引起混乱?
法赫尔

2
@Fakher要@Expose正常工作,您需要设置@Expose(serialize = false)并确保调用excludeFieldsWithoutExposeAnnotation()您的GsonBuilder对象。
Ibrahim.H,

1
这句话完美地解决了我的问题。使用new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create().toJson(YOUR_OBJECT),效果很好
Eason PI

如果您使用的是Kotlin,则有一个注释:kotlin /** * Marks the JVM backing field of the annotated property as `transient`, meaning that it is not * part of the default serialized form of the object. */ @Target(FIELD) @Retention(AnnotationRetention.SOURCE) @MustBeDocumented public actual annotation class Transient
AouledIssa

67

这有点晚了,但我也遇到了同样的问题。唯一的事情是我不能修改超类,因为那不是我的代码。我解决此问题的方法是创建一个排除策略,该策略将跳过任何在超类中具有相同名称的字段的字段。这是我该课程的代码:

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
这是正确的答案,因为我不想从序列化中排除超类的对象,我也不希望GSON也排除扩展类的变量
CQM

13
我认为这是错误的方法。这样,您的json将包含超类的变量,并且子类的值将被隐藏。人们会期望反过来。在json中有子类变量,并隐藏超类变量。
Veda 2015年

@Veda,您是否提出了解决方案的另一种方法?我正面临需要子变量和值的相同情况。
学习者

我想做反演,您需要知道您正在查看的类型是否是另一种类型的子类型。(如果可能的话)从继承树的另一端看似乎要困难得多。stackoverflow.com/questions/492184/…–
jocull

可接受的答案效果更好。如果顶级类将字段标记为瞬态,即使它与下级类具有相同的名称,则该字段也将从序列化中排除。
CompEng88

15

如果您具有不同的字段,但它们具有相同的,也会发生相同的错误消息@SerializedName

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

复制/粘贴您可以简单地犯这样的错误。因此,请调查该类及其祖先,并进行检查。


1
如果您的JSON文件确实确实具有相同名称且具有不同类型(故意)该怎么办?即某些端点“ main_id”是一个整数,其他端点“ main_id”是一个字符串
Ben Akin

1.您不能有两个具有相同名称的字段。2.您不能有两个具有相同序列化名称的字段。3.类型与这些规则无关。
Gangnus

7

在proguard.config的底部添加以下行 (如果您在项目中使用proguard)

-keepclassmembers class * {
    private <fields>;    
}

2

在我的情况下,我很笨,无法使用X类注册适配器,并尝试使用Y类将fromJson序列化:

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

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

2

我使用GsonBuilderExclusionStrategy避免了如下所示的冗余字段,这很简单明了。

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

此解决方案无需修改父类和子类中的字段即可工作。
詹妮弗(Jennifer)

1

根据@ Adrian-Lee的建议,为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
        }

    }
}

0

我不认为您应该使成员成为临时成员,这可能会导致错误,因为将来可能需要的成员可能会被隐藏。

我如何解决此问题的方法是使用自定义命名策略,并将完整的类名附加到Json,这样做的缺点是会导致Json更大,如果您需要它来使用Rest Api之类的东西,那将会很奇怪客户端以这种方式命名字段,但我只需要序列化即可写入android上的磁盘。

所以这是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}"
    }
}

因此,对于所有字段,将附加完整的规范名称,这将使子类的名称与父类的名称不同,但是在反序列化时,将使用子类的值。


也许这与@Adrian的解决方案结合起来应该可以工作
Gavriel19年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.