A类声明多个JSON字段

时间:2013-05-10 06:35:57

标签: java json gson

我有一个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类的对象

9 个答案:

答案 0 :(得分:60)

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

我不认为这个陈述是正确的,GSON在序列化时会查看对象的私有字段,这意味着包含了超类的所有私有字段,当你有相同名称的字段时会抛出错误。

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

private transient BigDecimal tradeFeesPcy;

答案 1 :(得分:57)

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

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

希望这有助于某人!

答案 2 :(得分:9)

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

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

复制/粘贴你可以犯这样的错误。因此,查看类及其祖先并检查它。

答案 3 :(得分:7)

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

-keepclassmembers class * {
    private <fields>;    
}

答案 4 :(得分: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();

答案 5 :(得分: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
        }

    }
}

答案 6 :(得分:0)

在我的情况下,我愚蠢到用X类注册一个适配器,并尝试使用Y类从json序列化:

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

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

答案 7 :(得分: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}"
    }
}

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

答案 8 :(得分:0)

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