[java] Gson : 주석없이 직렬화에서 특정 필드를 제외하는 방법

나는 Gson을 배우려고 노력하고 있으며 필드 배제로 고심하고 있습니다. 여기 내 수업이 있습니다

public class Student {
  private Long                id;
  private String              firstName        = "Philip";
  private String              middleName       = "J.";
  private String              initials         = "P.F";
  private String              lastName         = "Fry";
  private Country             country;
  private Country             countryOfBirth;
}

public class Country {
  private Long                id;
  private String              name;
  private Object              other;
}

GsonBuilder를 사용하고 firstName또는 같은 필드 이름에 ExclusionStrategy를 추가 할 수는 있지만와 같은 country특정 필드의 속성을 제외시키는 것은 관리 할 수 ​​없습니다 country.name.

이 메소드를 사용하면 public boolean shouldSkipField(FieldAttributes fa)FieldAttributes에 필터와 같은 필드를 일치시키기에 충분한 정보가 없습니다 country.name.

추신 : 나는 이것을 개선하고 RegEx를 사용하여 필드를 필터링하기 때문에 주석을 피하고 싶습니다.

편집 : Struts2 JSON 플러그인 의 동작을 에뮬레이트 할 수 있는지 확인하려고합니다.

Gson 사용

<interceptor-ref name="json">
  <param name="enableSMD">true</param>
  <param name="excludeProperties">
    login.password,
    studentList.*\.sin
  </param>
</interceptor-ref>

편집 :
다음을 추가하여 질문을 다시 열었습니다.

이 문제를 더 명확히하기 위해 동일한 유형의 두 번째 필드를 추가했습니다. 기본적으로 제외하고 싶지는 country.name않습니다 countrOfBirth.name. 또한 국가를 유형으로 제외하고 싶지 않습니다. 따라서 유형은 객체 그래프에서 정확히 지적하고 제외하려는 실제 위치와 동일합니다.



답변

일반적으로 직렬화하지 않으려는 모든 필드는 “일시적”수정자를 사용해야하며 이는 json 직렬화 기에도 적용됩니다 (적어도 gson을 포함하여 내가 사용한 몇 가지에 해당).

직렬화 된 json에 이름을 표시하지 않으려면 임시 키워드를 지정하십시오 (예 :

private transient String name;

Gson 문서에 대한 자세한 내용


답변

Nishant는 좋은 솔루션을 제공했지만 더 쉬운 방법이 있습니다. 다음과 같이 @Expose 주석으로 원하는 필드를 표시하십시오.

@Expose private Long id;

직렬화하지 않으려는 필드는 제외하십시오. 그런 다음 Gson 객체를 다음과 같이 만듭니다.

Gson gson = new GsonBuilder().excludeFieldsWithoutExposeAnnotation().create();


답변

따라서 및 을 제외 firstName 하고 싶습니다 country.name. 당신의 ExclusionStrategy모습은 다음과 같습니다.

    public class TestExclStrat implements ExclusionStrategy {

        public boolean shouldSkipClass(Class<?> arg0) {
            return false;
        }

        public boolean shouldSkipField(FieldAttributes f) {

            return (f.getDeclaringClass() == Student.class && f.getName().equals("firstName"))||
            (f.getDeclaringClass() == Country.class && f.getName().equals("name"));
        }

    }

당신이 자세히 볼 경우 반환 true을 위해 Student.firstNameCountry.name제외 할 무엇이다.

이런 ExclusionStrategy식 으로 적용해야합니다

    Gson gson = new GsonBuilder()
        .setExclusionStrategies(new TestExclStrat())
        //.serializeNulls() <-- uncomment to serialize NULL fields as well
        .create();
    Student src = new Student();
    String json = gson.toJson(src);
    System.out.println(json);

이것은 다음을 반환합니다.

{ "middleName": "J.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91}}

국가 객체가 id = 91L학생 클래스에서 초기화되었다고 가정합니다 .


당신은 공상을 얻을 수 있습니다. 예를 들어, 이름에 “name”문자열이 포함 된 필드를 직렬화하지 않으려 고합니다. 이 작업을 수행:

public boolean shouldSkipField(FieldAttributes f) {
    return f.getName().toLowerCase().contains("name");
}

이것은 다음을 반환합니다 :

{ "initials": "P.F", "country": { "id": 91 }}

편집 : 요청에 따라 더 많은 정보가 추가되었습니다.

이 작업 ExclusionStrategy이 수행되지만 “완전한 필드 이름”을 전달해야합니다. 아래를보십시오 :

    public class TestExclStrat implements ExclusionStrategy {

        private Class<?> c;
        private String fieldName;
        public TestExclStrat(String fqfn) throws SecurityException, NoSuchFieldException, ClassNotFoundException
        {
            this.c = Class.forName(fqfn.substring(0, fqfn.lastIndexOf(".")));
            this.fieldName = fqfn.substring(fqfn.lastIndexOf(".")+1);
        }
        public boolean shouldSkipClass(Class<?> arg0) {
            return false;
        }

        public boolean shouldSkipField(FieldAttributes f) {

            return (f.getDeclaringClass() == c && f.getName().equals(fieldName));
        }

    }

일반적인 사용법은 다음과 같습니다.

    Gson gson = new GsonBuilder()
        .setExclusionStrategies(new TestExclStrat("in.naishe.test.Country.name"))
        //.serializeNulls()
        .create();
    Student src = new Student();
    String json = gson.toJson(src);
    System.out.println(json); 

다음을 반환합니다.

{ "firstName": "Philip" , "middleName": "J.", "initials": "P.F", "lastName": "Fry", "country": { "id": 91 }}


답변

사용 가능한 모든 답변을 읽은 후 가장 유연한 것은 사용자 정의 @Exclude주석 을 사용하는 것 입니다. 그래서 나는 이것을 위해 간단한 전략을 구현했다 (나는 모든 필드를 사용하여 표시하고 싶지 않았고 앱 직렬화 에서 충돌하는 @Expose것을 사용 하고 싶지 않았다 ).transientSerializable

주석:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Exclude {
}

전략:

public class AnnotationExclusionStrategy implements ExclusionStrategy {

    @Override
    public boolean shouldSkipField(FieldAttributes f) {
        return f.getAnnotation(Exclude.class) != null;
    }

    @Override
    public boolean shouldSkipClass(Class<?> clazz) {
        return false;
    }
}

용법:

new GsonBuilder().setExclusionStrategies(new AnnotationExclusionStrategy()).create();


답변

나는이 문제에 부딪쳤다.이 필드에는 직렬화에서만 제외하고 싶었던 소수의 필드가 있었으므로 @Expose맞춤형 제외 전략과 함께 Gson의 주석 을 사용하는 상당히 간단한 솔루션을 개발했다 .

사용하는 기본 제공 방법 @Expose은을 설정하는 GsonBuilder.excludeFieldsWithoutExposeAnnotation()것이지만 이름에서 알 수 있듯이 명시 적 필드가없는 필드 @Expose는 무시됩니다. 제외 할 필드가 몇 개 밖에 없었기 때문에 모든 필드에 주석을 추가 할 가능성이 매우 성가신 것으로 나타났습니다.

나는 @Expose그것을 배제하기 위해 명시 적으로 사용하지 않는 한 모든 것이 포함 된 역수를 효과적으로 원 했습니다. 이를 달성하기 위해 다음 제외 전략을 사용했습니다.

new GsonBuilder()
        .addSerializationExclusionStrategy(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes fieldAttributes) {
                final Expose expose = fieldAttributes.getAnnotation(Expose.class);
                return expose != null && !expose.serialize();
            }

            @Override
            public boolean shouldSkipClass(Class<?> aClass) {
                return false;
            }
        })
        .addDeserializationExclusionStrategy(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes fieldAttributes) {
                final Expose expose = fieldAttributes.getAnnotation(Expose.class);
                return expose != null && !expose.deserialize();
            }

            @Override
            public boolean shouldSkipClass(Class<?> aClass) {
                return false;
            }
        })
        .create();

이제 몇 개의 필드 @Expose(serialize = false)또는 @Expose(deserialize = false)주석을 쉽게 제외 할 수 있습니다 (두 @Expose속성 의 기본값 은입니다 true). 물론 사용할 수 @Expose(serialize = false, deserialize = false)있지만 필드를 transient대신 선언하여 더 간결하게 수행 할 수 있습니다 (이러한 사용자 지정 제외 전략에는 여전히 적용됨).


답변

gson으로 json 트리를 탐색 할 수 있습니다.

다음과 같이 해보십시오.

gson.toJsonTree(student).getAsJsonObject()
.get("country").getAsJsonObject().remove("name");

당신은 또한 몇 가지 속성을 추가 할 수 있습니다 :

gson.toJsonTree(student).getAsJsonObject().addProperty("isGoodStudent", false);

gson으로 테스트 2.2.4.


답변

이 기능을 지원하기 위해 클래스 팩토리를 고안했습니다. 제외하려는 필드 또는 클래스의 조합을 전달하십시오.

public class GsonFactory {

    public static Gson build(final List<String> fieldExclusions, final List<Class<?>> classExclusions) {
        GsonBuilder b = new GsonBuilder();
        b.addSerializationExclusionStrategy(new ExclusionStrategy() {
            @Override
            public boolean shouldSkipField(FieldAttributes f) {
                return fieldExclusions == null ? false : fieldExclusions.contains(f.getName());
            }

            @Override
            public boolean shouldSkipClass(Class<?> clazz) {
                return classExclusions == null ? false : classExclusions.contains(clazz);
            }
        });
        return b.create();

    }
}

사용하려면 두 개의 목록을 만들고 (각각 선택 사항 임) GSON 객체를 만듭니다.

static {
 List<String> fieldExclusions = new ArrayList<String>();
 fieldExclusions.add("id");
 fieldExclusions.add("provider");
 fieldExclusions.add("products");

 List<Class<?>> classExclusions = new ArrayList<Class<?>>();
 classExclusions.add(Product.class);
 GSON = GsonFactory.build(null, classExclusions);
}

private static final Gson GSON;

public String getSomeJson(){
    List<Provider> list = getEntitiesFromDatabase();
    return GSON.toJson(list);
}