codememo

클래스 A가 복수의 JSON 필드를 선언합니다.

tipmemo 2023. 3. 29. 21:33
반응형

클래스 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

어떻게 고칠까요?

private 필드이기 때문에 json string을 작성할 때는 문제가 없을 것이라고 생각합니다만, 잘 모르겠습니다.

다음과 같은 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;

복사/붙여넣기를 하면 이러한 실수를 저지를 수 있습니다.그래서 계급과 그 조상들을 조사해서 확인해보세요.

  1. 같은 이름의 필드는 두 개일 수 없습니다.
  2. 동일한 일련 이름을 가진 두 개의 필드를 가질 수 없습니다.
  3. 유형은 이러한 규칙과 무관합니다.

하였습니다.GsonBuilder ★★★★★★★★★★★★★★★★★」ExclusionStrategy다음과 같은 중복 필드를 피하기 위해 단순하고 직설적입니다.

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

proguard.config 하단에 다음 행을 추가합니다(프로가드를 프로젝트에서 사용하는 경우).

-keepclassmembers class * {
    private <fields>;    
}

멤버를 임시로 만들면 안 된다고 생각합니다.앞으로 필요한 멤버가 숨겨져 있을 수 있기 때문에 에러가 발생할 수 있습니다.

이 문제를 해결한 방법은 커스텀 네임 스트래티지를 사용하여 풀 클래스 이름을 Json에 추가하는 것입니다.단점으로는 큰 Json을 얻을 수 있고 Rest API와 같은 것을 위해 필요한 경우 클라이언트는 이러한 필드 이름을 그렇게 짓는 것이 이상하지만 저는 안드로이드에서 디스크에 쓰기 위해 시리얼화만 하면 됩니다.

여기 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}"
    }
}

따라서 모든 필드에 대해 완전한 표준 이름이 추가되며, 이로 인해 자녀 클래스는 부모 클래스와 다른 이름을 가지지만, 역직렬화 시 자녀 클래스 값이 사용됩니다.

Kotlin에서 추가되는@Transient부모 클래스의 변수에 대한 주석이 나에게 트릭을 주었다.sealed클래스(개방 변수 포함.

@Adrian-Lee 제안대로 Kotlin의 솔루션은 몇 가지 특수한 체크를 조정해야 합니다.

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
        }

    }
}

내 경우 어댑터를 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);

Kotliner의 경우:

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

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

반응형