developer tip

클래스 A는 여러 JSON 필드를 선언합니다.

optionbox 2020. 11. 20. 08:53
반응형

클래스 A는 여러 JSON 필드를 선언합니다.


일부 개인 필드가있는 클래스 A가 있고 동일한 클래스가 클래스 A에있는 일부 개인 필드가있는 다른 클래스 B를 확장합니다.

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의 객체입니다.


개인 필드이므로 json 문자열을 생성하는 동안 문제가 없어야합니다.

이 진술이 사실이라고 생각하지 않습니다. GSON은 직렬화 할 때 개체의 개인 필드를 조회합니다. 즉, 수퍼 클래스의 모든 개인 필드가 포함되어 있고 동일한 이름의 필드가 있으면 오류가 발생합니다.

포함하고 싶지 않은 특정 필드가있는 경우 transient키워드 로 표시해야 합니다. 예 :

private transient BigDecimal tradeFeesPcy;

이것은 조금 늦었지만 똑같은 문제가 발생했습니다. 유일한 것은 해당 코드가 내 것이 아니기 때문에 수퍼 클래스를 수정할 수 없다는 것입니다. 이 문제를 해결 한 방법은 수퍼 클래스에 동일한 이름의 필드가있는 필드를 건너 뛰는 제외 전략을 만드는 것이 었습니다. 해당 클래스에 대한 내 코드는 다음과 같습니다.

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

그런 다음 빌더에서 Serialization 및 Deserialization 제외 전략을 다음과 같이 설정합니다.

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

바라건대 이것은 누군가를 도울 것입니다!


필드가 다른 경우에도 동일한 오류 메시지가 발생하지만 @SerializedName.

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

복사 / 붙여 넣기를하면 이러한 실수를 할 수 있습니다. 따라서 클래스 와 그 조상을 살펴보고 확인하십시오.


Add following lines at the bottom of proguard.config (if you are using proguard in project)

-keepclassmembers class * {
    private <fields>;    
}

Solution for Kotlin, as suggested @Adrian-Lee, you have to tweak some 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
        }

    }
}

I don't think you should make the members transient, this might lead to errors because members that you might need in the future might be hidden.

How I solved this problem is to use a custom naming strategy and append the full class name to the Json, the downside of this is that it would lead to larger Json and if you need it for something like a Rest Api it would be weird for clients to name the fields that way, but I only needed to serialize to write to disk on android.

So here is an implementation of a custom naming strategy in 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}"
    }
}

So for all fields, the full canonical name would be appended, this would make the child class have a different name from the parent class, but when deserializing, the child class value would be used.


In my case I was dumb enough to register an adapter with X class, and try to serialize fromJson with Y class:

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

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

참고URL : https://stackoverflow.com/questions/16476513/class-a-declares-multiple-json-fields

반응형