java / / 2023. 1. 10. 20:43

Jackson Annotation

아래 내용은 baeldung에 있는 내용을 정리한 것이다. (https://www.baeldung.com/jackson-annotations)

1. Jackson Serialization Annotation

1.1. @JsonAnyGetter

@JsonAnyGetter 어노테이션은 맵을 기본 필드로 동작하게 한다.

예를 들면, ExtendableBean은 name과 key/value 의 값을 가진 속성들로 구성되어 있다.

public class ExtendableBean {
    public String name;
    private Map<String, String> properties;

    public static void main(String[] args) {
        ExtendableBean bean = new ExtendableBean("My Bean");
        bean.add("attr1", "val1");
        bean.add("attr2", "val2");

        System.out.println(bean.toString());
    }
}

기본적으로 위의 객체를 직렬화하면 아래와 같이 나타날 것이다.

{"name":"My Bean","properties":{"attr2":"val2","attr1":"val1"}}

하지만 @JsonAnyGetter를 사용하면 Map이 필드 속성으로 직렬화된다.

public class ExtendableBean {

    public String name;

    private Map<String, String> properties;

    public ExtendableBean(String name) {
        this.name = name;
        this.properties = new HashMap<>();
    }

    @JsonAnyGetter
    public Map<String, String> getProperties() {
        return properties;
    }

    public static void main(String[] args) {
        ExtendableBean bean = new ExtendableBean("My Bean");
        bean.add("attr1", "val1");
        bean.add("attr2", "val2");

        System.out.println(bean.toString());
    }
}

결과

{"name":"My Bean","attr2":"val2","attr1":"val1"}

1.2. @JsonGetter

@JsonGetter는 getter 메소드를 나타내는 @JsonProperty의 대안이다.

아래 예를 보자.

@AllArgsConstructor
public class MyBean {
    public int id;
    private String name;

    public String getTheName() {
        return name;
    }

    public static void main(String[] args) throws JsonProcessingException {
        MyBean bean = new MyBean(123, "홍길동");
        String result = new ObjectMapper().writeValueAsString(bean);
        System.out.println(result); // {"id":123,"theName":"홍길동"}
    }
}

MyBean을 직렬화하면 name은 theName 필드로 직렬화된다.

theName을 name으로 변경하고자 하면 @JsonGetter를 사용하면 된다.

@AllArgsConstructor
public class MyBean {
    public int id;
    private String name;

    @JsonGetter("name")
    public String getTheName() {
        return name;
    }

    public static void main(String[] args) throws JsonProcessingException {
        MyBean bean = new MyBean(123, "홍길동");
        String result = new ObjectMapper().writeValueAsString(bean);
        System.out.println(result); // {"id":123,"name":"홍길동"}
    }
}

1.3. @JsonPropertyOrder

직렬화할 때 프로퍼티의 순서를 명시하고자 할 때 @JsonPropertyOrder를 사용할 수 있다.

@AllArgsConstructor
@Getter
public class MyBeanOrder {
    private int id;
    private String name;
    private String address;

    public static void main(String[] args) throws JsonProcessingException {
        MyBeanOrder bean = new MyBeanOrder(123, "홍길동", "서울");
        String result = new ObjectMapper().writeValueAsString(bean);
        System.out.println(result); // {"id":123,"name":"홍길동","address":"서울"}
    }
}

위의 같이 필드의 순서대로 직렬화가 된다. // {"id":123,"name":"홍길동","address":"서울"}

순서를 변경하려면 아래와 같이 @JsonPropertyOrder를 사용하면 된다.

@AllArgsConstructor
@Getter
@JsonPropertyOrder({ "name", "address", "id" })
public class MyBeanOrder {
    private int id;
    private String name;
    private String address;

    public static void main(String[] args) throws JsonProcessingException {
        MyBeanOrder bean = new MyBeanOrder(123, "홍길동", "서울");
        String result = new ObjectMapper().writeValueAsString(bean);
        System.out.println(result); // {"name":"홍길동","address":"서울","id":123}
    }
}

1.4. @JsonRawValue

@JsonRawValue 어노테이션은 jackson으로 하여금 속성 그대로 직렬화하게 한다.

@AllArgsConstructor
@Getter
public class RawBean {
    private String name;

    public String json;

    public static void main(String[] args) throws JsonProcessingException {
        RawBean bean = new RawBean("My bean", "{\"attr\":false}");
        String result = new ObjectMapper().writeValueAsString(bean);
        System.out.println(result); // {"name":"My bean","json":"{\"attr\":false}"}
    }
}

위와 같이 json은 하나의 문자열로 직렬화가 된다.

하지만 json이 json문자열 형태로 직렬화하고 싶다면 아래와 같이 @JsonRawValue를 사용하면 된다.

@AllArgsConstructor
@Getter
public class RawBean {
    private String name;

    @JsonRawValue
    public String json;

    public static void main(String[] args) throws JsonProcessingException {
        RawBean bean = new RawBean("My bean", "{\"attr\":false}");
        String result = new ObjectMapper().writeValueAsString(bean);
        System.out.println(result); // {"name":"My bean","json":{"attr":false}}
    }
}

@JsonRawValue를 사용하면 json값이 json형태로 직렬화가 된다.

1.5. @JsonValue

@JsonValue는 특정 필드를 직렬화할 때 사용한다.

public enum TypeEnumWithValue {
    TYPE1(1, "Type A"), TYPE2(2, "Type 2");

    private Integer id;
    private String name;

    TypeEnumWithValue(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public static void main(String[] args) throws JsonProcessingException {
        TypeEnumWithValue typeEnumWithValue = TypeEnumWithValue.TYPE1;
        String result = new ObjectMapper().writeValueAsString(typeEnumWithValue);
        System.out.println(result); // "TYPE1"
    }
}

위의 TypeEnumWithValue를 직렬화하면 TYPE1의 enum이 출력된다.

위에서 name이 출력되게 하고 싶다면 @JsonValue를 사용하면 된다.

public enum TypeEnumWithValue {
    TYPE1(1, "Type A"), TYPE2(2, "Type 2");

    private Integer id;
    private String name;

    @JsonValue
    public String getName() {
        return name;
    }

    TypeEnumWithValue(int id, String name) {
        this.id = id;
        this.name = name;
    }

    public static void main(String[] args) throws JsonProcessingException {
        TypeEnumWithValue typeEnumWithValue = TypeEnumWithValue.TYPE1;
        String result = new ObjectMapper().writeValueAsString(typeEnumWithValue);
        System.out.println(result); // "Type A"
    }
}

1.6. @JsonRootName

상위 래퍼의 이름을 명시하고자 할 때 @JsonRootName 어노테이션이 사용된다.

@Getter
@AllArgsConstructor
public class UserWithRoot {
    public int id;
    public String name;

    public static void main(String[] args) throws JsonProcessingException {
        UserWithRoot root = new UserWithRoot(123, "홍길동");
        String result = new ObjectMapper().writeValueAsString(root);
        System.out.println(result); // {"id":123,"name":"홍길동"}
    }
}

위의 UserWithRoot를 직렬화하면 {"id":123,"name":"홍길동"}으로 직렬화되지만 상위에 특정 이름을 부여하고 싶다면 아래와 같이 @JsonRootName을 사용하면 된다.

@Getter
@AllArgsConstructor
@JsonRootName(value = "user")
public class UserWithRoot {
    public int id;
    public String name;

    public static void main(String[] args) throws JsonProcessingException {
        UserWithRoot root = new UserWithRoot(123, "홍길동");
        ObjectMapper mapper = new ObjectMapper();
        mapper.enable(SerializationFeature.WRAP_ROOT_VALUE); // WRAP_ROOT_VALUE 설정을 해줘야 함
        String result = mapper.writeValueAsString(root);
        System.out.println(result); // {"user":{"id":123,"name":"홍길동"}}
    }
}

2. Jackson Deserialization Annotation

2.1 @JsonCreator

역직렬화할 때 생성자/팩토리를 조정할 때 @JsonCreator를 사용할 수 있다.

다음과 같은 JSON형태로 역직렬화할 필요가 있다고 하자.

{
    "id":1,
    "theName":"My bean"
}

하지만, 필드에 theName은 없고 name필드가 있다. 필드 자체를 수정하고 싶지 않고 단지 @JsonCreator를 통해서만 사용하고 싶다.

@Getter
public class BeanWithCreator {
    private int id;
    private String name;

    @JsonCreator
    public BeanWithCreator(
            @JsonProperty("id") int id,
            @JsonProperty("theName") String name
    ) {
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws JsonProcessingException {
        String json = "{\"id\":1,\"theName\":\"My bean\"}";
        BeanWithCreator bean = new ObjectMapper().readerFor(BeanWithCreator.class).readValue(json);
        System.out.println(bean.toString());
    }
}

2.2 @JacksonInject

@JacksonInject는 필드가 JSON 데이터가 아니라 주입된 값임을 알려준다.

@Getter
public class BeanWithInject {
    //@JacksonInject
    private int id;

    private String name;

    @Override
    public String toString() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws JsonProcessingException {
        String json = "{\"name\":\"My bean\"}";
        InjectableValues inject = new InjectableValues.Std()
                .addValue(int.class, 1);
        BeanWithInject bean = new ObjectMapper().reader(inject)
                .forType(BeanWithInject.class)
                .readValue(json);

        System.out.println(bean.toString());
    }
}

위 코드를 실행할 때 @JacksonInject가 있는 경우와 없는 경우의 결과는 아래와 같다.

없는 경우

{"id":0,"name":"My bean"}

있는 경우

{"id":1,"name":"My bean"}

2.3 @JsonAnySetter

@JsonAnySetter는 Map을 기본 프로퍼티로 사용할 수 있게 한다. 역직렬화할 때 JSON의 프로퍼티는 단순히 맵(Map)에 추가될 것이다.

@Getter
public class ExtendableBean {

    private String name;

    private Map<String, String> properties = new HashMap<>();

    @JsonAnySetter
    public void add(String key, String value) {
        this.properties.put(key, value);
    }

    @JsonAnyGetter
    public Map<String, String> getProperties() {
        return properties;
    }


    public String toString() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws JsonProcessingException{
        String json = "{\"name\":\"My bean\",\"attr2\":\"val2\",\"attr1\":\"val1\"}";
        ExtendableBean bean = new ObjectMapper()
                .readerFor(ExtendableBean.class)
                .readValue(json);

        System.out.println(bean.toString());
    }

2.4 @JsonSetter

@JsonSetter는 setter 메소드를 나타내는 @JsonProperty의 대안이다.

JSON 데이터를 읽을 때 매우 유용하지만 대상 엔티티 클래스에 정확히 매칭되지 않고 몇가지 작업이 필요할 수도 있다.

@Getter
public class MyBean {
    private int id;
    private String name;

    @JsonSetter("name")
    public void setTheName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws JsonProcessingException {
        String json = "{\"id\":1,\"name\":\"My bean\"}";

        MyBean bean = new ObjectMapper()
                .readerFor(MyBean.class)
                .readValue(json);

        System.out.println(bean.toString()); // {"id":1,"name":"My bean"}
    }
}

2.5 @JsonAlias

@JsonAlias는 역직렬화할 때 하나 이상의 이름을 정의할 때 사용된다.

@Getter
public class AliasBean {
    @JsonAlias({ "fName", "f_name" })
    private String firstName;
    private String lastName;

    @Override
    public String toString() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws JsonProcessingException {
        String json = "{\"fName\": \"John\", \"lastName\": \"Green\"}";
        AliasBean aliasBean = new ObjectMapper()
                .readerFor(AliasBean.class)
                .readValue(json);

        System.out.println(aliasBean.toString());
    }
}

위의 코드에서 fName는 필드에 없다. fName을 필드에 매핑을 하려고 한다면 @JsonAlias를 사용하면 된다.

@JsonAlias없이 사용하면 UnrecognizedPropertyException이 발생할 것이다.

위에서 fNamef_namefirstName으로 매핑을 하겠다는 의미이다.


3. Jackson Property Inclusion Annotations

3.1 @JsonIgnoreProperties

@JsonIgnoreProperties는 Jackson이 무시할 프로퍼티를 표시하는 클래스 레벨의 어노테이션이다.

@Getter
@AllArgsConstructor
public class BeanWithIgnore {
    private int id;
    private String name;

    @Override
    public String toString() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        BeanWithIgnore bean = new BeanWithIgnore(123, "홍길동");
        System.out.println(bean.toString()); // {"id":123,"name":"홍길동"}
    }
}

위의 코드에서 BeanWithIgnore를 출력하면 {"id":123,"name":"홍길동"}로 표시될 것이다.

여기서 id는 제외하고 출력하고 싶다면 @JsonIgnoreProperties를 사용하면 된다.

@JsonIgnoreProperties({ "id" })
@Getter
@AllArgsConstructor
public class BeanWithIgnore {
    private int id;
    private String name;

    @Override
    public String toString() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        BeanWithIgnore bean = new BeanWithIgnore(123, "홍길동");
        System.out.println(bean.toString()); // {"name":"홍길동"}
    }
}

JSON 입력값이 예외없이 정의되지 않은 특정 프로퍼티를 제외하려면, ignoreUnknown=true로 설정하면 된다.

3.2 @JsonIgnore

@JsonIgnore 어노테이션은 필드 수준에서 무시할 프로퍼티를 표시하는데 사용된다.

@Getter
@AllArgsConstructor
public class BeanWithIgnoreField {
    @JsonIgnore // 이 필드는 역직렬화할 때 무시된다.
    public int id;

    public String name;

    @Override
    public String toString() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        BeanWithIgnoreField bean = new BeanWithIgnoreField(123, "홍길동");
        System.out.println(bean.toString()); // {"name":"홍길동"}
    }
}

3.3 @JsonIgnoreType

@JsonIgnoreType은 프로퍼티에서 무시될 어노테이션 타입을 표시한다.

아래 코드에서 Name을 무시하려면 @JsonIgnoreType을 사용하면 된다.

@Getter
@AllArgsConstructor
public class User {
    private int id;
    private Name name;

    @JsonIgnoreType
    @AllArgsConstructor
    public static class Name {
        public String firstName;
        public String lastName;
    }

    @Override
    public String toString() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        User user = new User(123, new User.Name("길동", "홍"));
        System.out.println(user.toString());
    }
}

3.4 @JsonInclude

공백, null값, 기본값인 프로퍼티를 제외할 때 @JsonInclude를 사용한다.

@Getter
@AllArgsConstructor
public class MyBean {
    private int id;
    private String name;

    @Override
    public String toString() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        MyBean bean = new MyBean(123, null);
        System.out.println(bean.toString()); // {"id":123,"name":null}
    }
}

위의 코드를 출력하면 {"id":123,"name":null} 으로 표시가 된다. 여기서 name은 null이므로 name을 역직렬화에서 제외하고 싶을 경우에 @JsonInclude를 사용하면 된다.

@JsonInclude(JsonInclude.Include.NON_NULL)
@Getter
@AllArgsConstructor
public class MyBean {
    private int id;
    private String name;

    @Override
    public String toString() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        MyBean bean = new MyBean(123, null);
        System.out.println(bean.toString()); // {"id":123}
    }
}

3.5 @JsonAutoDetect

@JsonAutoDetect는 프로퍼티의 가시성 여부와 상관없이 사용할 수 있다.

@AllArgsConstructor
public class PrivateBean {
    private int id;
    private String name;

    @Override
    public String toString() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        PrivateBean bean = new PrivateBean(123, "홍길동");
        System.out.println(bean.toString()); // No serializer found for class com.example.jackson_annotation.inclusion.PrivateBean and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)
    }
}

위의 코드를 역직렬화하면 프로퍼티가 없어서 오류를 발생시킨다. 그래서 프로퍼티의 가시성과 여부와 상관없이 역직렬화하려면 @JsonAutoDetect를 사용하면 된다.

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY)
@AllArgsConstructor
public class PrivateBean {
    private int id;
    private String name;

    @Override
    public String toString() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        PrivateBean bean = new PrivateBean(123, "홍길동");
        System.out.println(bean.toString()); // {"id":123,"name":"홍길동"}
    }
}

4. Jackson Polymorphic Type Handling Annotations

Jackson의 다형성 관련 어노테이션을 살펴보자.

  • @JsonTypeInfo - 직렬화 때 포함될 상세 정보
  • @JsonSubTypes - 어노테이션 유형의 하위 유형
  • @JsonTypeName - 어노테이션 클래스로 사용할 이름을 정의
@AllArgsConstructor
@NoArgsConstructor
@Getter
public class Zoo {
    private Animal animal;

    @JsonTypeInfo(
            use = JsonTypeInfo.Id.NAME,
            include = JsonTypeInfo.As.PROPERTY,
            property = "type")
    @JsonSubTypes({
            @JsonSubTypes.Type(value = Dog.class, name = "dog"),
            @JsonSubTypes.Type(value = Cat.class, name = "cat")
    })
    @AllArgsConstructor
    @NoArgsConstructor
    public static class Animal {
        public String name;
    }

    @JsonTypeName("dog")
    @NoArgsConstructor
    public static class Dog extends Animal {
        public double barkVolume;

        public Dog(String name, double barkVolume) {
            super(name);
            this.barkVolume = barkVolume;
        }
    }

    @JsonTypeName("cat")
    @NoArgsConstructor
    public static class Cat extends Animal {
        boolean likesCream;
        public int lives;

        public Cat(String name, boolean likesCream, int lives) {
            super(name);
            this.likesCream = likesCream;
            this.lives = lives;
        }
    }

    public static void main(String[] args) throws JsonProcessingException {
        Zoo.Dog dog = new Zoo.Dog("lacy", 0);
        Zoo zoo = new Zoo(dog);

        String result = new ObjectMapper().writeValueAsString(zoo);
        System.out.println(result);
    }
}

위의 코드를 역직렬화하면 아래와 같을 것이다.

{
    "animal": {
        "type": "dog",
        "name": "lacy",
        "barkVolume": 0
    }
}

다시 아래 구조로 역직렬화를 해보자.

{
    "animal":{
        "name":"lacy",
        "type":"cat"
    }
}

아래와 같이 Cat이 출력될 것이다.

public static void main(String[] args) throws JsonProcessingException {
        String json = "{\"animal\":{\"name\":\"lacy\",\"type\":\"cat\"}}";

        Zoo zoo = new ObjectMapper()
                .readerFor(Zoo.class)
                .readValue(json);
        System.out.println(zoo.animal.getClass()); // class com.example.jackson_annotation.polymorphic.Zoo$Cat
    }

5. Jackson General Annotations

5.1 @JsonProperty

JSON에서 프로퍼티 이름을 나타내기 위해 @JsonProperty를 추가할 수 있다.

@Getter
@AllArgsConstructor
public class MyBean {
    private int id;
    private String name;

    @JsonProperty("name")
    public void setTheName(String name) {
        this.name = name;
    }

    @JsonProperty("name")
    public String getTheName() {
        return name;
    }

    @Override
    public String toString() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        MyBean bean = new MyBean(123, "홍길동");
        System.out.println(bean.toString());
    }
}

@JsonProperty를 사용하여 직렬화/역직렬화할 프로퍼티를 설정할 수 있다.

5.2 @JsonFormat

@JsonFormat 어노테이션은 Date/Time 값을 직렬화할 때 포맷을 지정할 때 사용할 수 있다.

@Getter
@AllArgsConstructor
public class EventWithFormat {
    private String name;

    @JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "dd-MM-yyyy hh:mm:ss")
    private Date eventDate;

    @Override
    public String toString() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) throws ParseException {
        SimpleDateFormat df = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
        df.setTimeZone(TimeZone.getTimeZone("UTC"));

        String toParse = "20-12-2014 02:30:00";
        Date date = df.parse(toParse);

        EventWithFormat event = new EventWithFormat("홍길동", date);
        System.out.println(event.toString()); // {"name":"홍길동","eventDate":"20-12-2014 02:30:00"}
    }
}

위의 코드에서 eventDate는 Date 타입인데 날짜 포맷을 특정 형식으로 표시하고자 할 때 @JsonFormat을 사용하면 된다.

5.3 @JsonUnwrapped

@JsonUnwrapped는 직렬화/역직렬화할 때 flat하게 표시하고자 할 때 사용된다.

@AllArgsConstructor
@Getter
public class UnwrappedUser {
    private int id;

    private Name name;

    @AllArgsConstructor
    @Getter
    public static class Name {
        public String firstName;
        public String lastName;
    }

    @Override
    public String toString() {
        ObjectMapper objectMapper = new ObjectMapper();
        try {
            return objectMapper.writeValueAsString(this);
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public static void main(String[] args) {
        UnwrappedUser user = new UnwrappedUser(123, new Name("길동", "홍"));
        System.out.println(user.toString());
    }
}

위의 코드를 출력하면 아래와 같은 형태가 될 것이다.

{
  "id":123,
  "name": {
    "firstName":"길동",
    "lastName":"홍"
  }
}

여기서 name을 제외하고 firstName, lastName을 1단계로 flat하게 표시하고자 하면 @JsonUnwrapped를 사용하면 된다.

public class UnwrappedUser {
    private int id;

    @JsonUnwrapped
    private Name name;

  ...
}

출력하면 아래와 같이 표시될 것이다.

{
  "id":123,
  "firstName":"길동",
  "lastName":"홍"
}

5.4 @JsonView

@JsonView는 직렬화/역직렬화에 포함될 프로퍼티의 뷰를 나타낸다.

@AllArgsConstructor
@Getter
public class Item {
    @JsonView(Views.Public.class)
    private int id;

    @JsonView(Views.Public.class)
    private String itemName;

    @JsonView(Views.Internal.class)
    private String ownerName;

    public static void main(String[] args) throws JsonProcessingException {
        Item item = new Item(2, "book", "John");
        String result = new ObjectMapper()
                .writerWithView(Views.Public.class)
                .writeValueAsString(item);
        System.out.println(result); // {"id":2,"itemName":"book"}
    }
}

위의 코드에서 writerWithView로 Views.Public을 설정하면 @JsonView에 Views.Public.class로 설정된 프로퍼티만 직렬화/역직렬화된다.

5.5 @JsonManagedReference, @JsonBackReference

@JsonManagedReference 어노테이션은 parent/child 관계를 처리할 때 사용된다.

@Getter
@AllArgsConstructor
public class ItemWithRef {
    private int id;
    private String itemName;

    public ItemWithRef(int id, String itemName) {
        this.id = id;
        this.itemName = itemName;
    }

    @JsonManagedReference
    public UserWithRef owner;

    public static void main(String[] args) throws JsonProcessingException {
        UserWithRef user = new UserWithRef(1, "John");
        ItemWithRef item = new ItemWithRef(2, "book", user);
        user.addItem(item);

        String result = new ObjectMapper().writeValueAsString(item);
        System.out.println(result);
    }
}
@Getter
@AllArgsConstructor
public class UserWithRef {
    private int id;
    private String name;

    public UserWithRef(int id, String name) {
        this.id = id;
        this.name = name;
        this.userItems = new ArrayList<>();
    }

    @JsonBackReference
    public List<ItemWithRef> userItems;

    public void addItem(ItemWithRef item) {
        this.userItems.add(item);
    }
}

위의 코드를 실행하면 아래와 같이 출력된다.

{"id":2,
    "itemName":"book",
    "owner": {
           "id":1,
        "name":"John"
    }
}

5.6 @JsonIdentityInfo

@JsonIdentityInfo는 직렬화/역직렬화 할 때 무한 루프 문제를 처리할 때 사용된다.

@JsonIdentityInfo(
        generator = ObjectIdGenerators.PropertyGenerator.class,
        property = "id")
@Getter
@AllArgsConstructor
public class ItemWithIdentity {
    private int id;
    private String itemName;
    private UserWithIdentity owner;

    public static void main(String[] args) throws JsonProcessingException {
        UserWithIdentity user = new UserWithIdentity(1, "John");
        ItemWithIdentity item = new ItemWithIdentity(2, "book", user);
        user.addItem(item);

        String result = new ObjectMapper().writeValueAsString(item);
        System.out.println(result);
    }
}
@JsonIdentityInfo(
        generator = ObjectIdGenerators.PropertyGenerator.class,
        property = "id")
@Getter
public class UserWithIdentity {
    private int id;
    private String name;
    private List<ItemWithIdentity> userItems;

    public UserWithIdentity(int id, String name) {
        this.id = id;
        this.name = name;
        this.userItems = new ArrayList<>();
    }

    public void addItem(ItemWithIdentity item) {
        this.userItems.add(item);
    }
}

위의 코드에서 @JsonIdentityInfo를 사용하지 않으면 무한루프가 발생한다.

5.7 @JsonFilter

@JsonFilter 어노테이션은 직렬화할 때 사용할 필터를 정의한다.

@JsonFilter("myFilter")
@Getter
@AllArgsConstructor
public class BeanWithFilter {
    private int id;
    private String name;

    public static void main(String[] args) throws JsonProcessingException {
        BeanWithFilter bean = new BeanWithFilter(123, "홍길동");
        FilterProvider filters
                = new SimpleFilterProvider().addFilter(
                "myFilter",
                SimpleBeanPropertyFilter.filterOutAllExcept("name"));
        String result = new ObjectMapper().writer(filters).writeValueAsString(bean);
        System.out.println(result);
    }
}

위와 같이 직렬화할 때 filter를 정의하고 사용할 수 있다.

반응형
  • 네이버 블로그 공유
  • 네이버 밴드 공유
  • 페이스북 공유
  • 카카오스토리 공유