83

我有一个 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 类的私有字段

现在,当我尝试从上面的 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 类的对象

4

10 回答 10

85

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

我不认为这种说法是正确的,GSON 在序列化时会查看对象的私有字段,这意味着包含超类的所有私有字段,并且当您有同名的字段时会引发错误。

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

private transient BigDecimal tradeFeesPcy;
于 2013-05-10T07:00:51.267 回答
71

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

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

希望这可以帮助某人!

于 2013-11-21T10:06:58.993 回答
16

如果您有不同的字段,但它们具有相同的@SerializedName.

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

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

  1. 您不能有两个具有相同名称的字段。
  2. 您不能有两个具有相同序列化名称的字段。
  3. 类型与这些规则无关。
于 2016-08-11T11:37:57.490 回答
7

我使用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();
于 2020-04-27T23:03:57.747 回答
6

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

-keepclassmembers class * {
    private <fields>;    
}
于 2017-12-22T07:22:32.630 回答
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);
于 2016-04-28T18:31:59.967 回答
1

Kotlin 的解决方案,正如@Adrian-Lee 所建议的那样,您必须调整一些空检查

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
        }

    }
}
于 2018-03-26T21:26:32.077 回答
1

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

我解决这个问题的方法是使用自定义命名策略并将完整的类名附加到 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}"
    }
}

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

于 2019-07-02T04:33:33.887 回答
1

对于 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()
于 2021-06-02T10:21:45.407 回答
0

在 kotlin@Transient中,为父类上的变量添加注释对我来说在sealed具有开放变量的类上起到了作用。

于 2021-11-05T11:31:53.910 回答