관리 메뉴

피터의 개발이야기

[Spring] Jackson Annotations 본문

Programming/Spring

[Spring] Jackson Annotations

기록하는 백앤드개발자 2020. 12. 10. 08:00
반응형

json관련하여 jackson 라이브러리를 사용하고 있다.

jackson에서 재공하는 유용한 어노테이션을 정리한다. 

 

Read+ Write Annotations

@JsonIgnore

public class TestIgnore {

    @JsonIgnore
    public long    testId = 0;

    public String  name = null;
}

위 코드에서 testId는 JSON에서 읽거나 기록되지 않는다.

 

@JsonIgnoreProperties

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;

@JsonIgnoreProperties({"firstName", "lastName"})
public class PersonIgnoreProperties {

    public long    personId = 0;

    public String  firstName = null;
    public String  lastName  = null;

}

@JsonIgnore과 마찬가지로 해당 필드리스트에 대해서 일거나 기록하지 않는다. 

 

@JsonIgnoreType

import com.fasterxml.jackson.annotation.JsonIgnoreType;

public class PersonIgnoreType {

    @JsonIgnoreType
    public static class Address {
        public String streetName  = null;
        public String houseNumber = null;
        public String zipCode     = null;
        public String city        = null;
        public String country     = null;
    }

    public long    personId = 0;

    public String  name = null;

    public Address address = null;
}

위의 예에서는 모든 Address인스턴스가 무시됩니다.

 

 

@JsonAutoDetect

import com.fasterxml.jackson.annotation.JsonAutoDetect;

@JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY )
public class PersonAutoDetect {

    private long  personId = 123;
    public String name     = null;

}

JsonAutoDetect.Visibility 클래스에는 접근제한 레벨 상수가 정의 되어 있다. 
 - ANY

 - DEFAULT

 - NON_PRIVATE

 - NONE

 - PROTECTED_AND_PRIVATE

 - PUBLIC_ONLY

 

Read Annotations

@JsonSetter

{
  "id"   : 1234,
  "name" : "John"
}

받고자 하는 JSON의 형태는 "id"이지만 Person에서는 "personId"에 맵핑할 수 있다. 

public class Person {

    private long   personId = 0;
    private String name     = null;

    public long getPersonId() { 
    	return this.personId; 
    }
    
    @JsonSetter("id")
    public void setPersonId(long personId) { 
    	this.personId = personId; 
    }

    public String getName() { 
		return name; 
    }
    
    public void setName(String name) { 
    	this.name = name; 
    }
}

 

@JsonAnySetter

받고자 하는 JSON의 형태에서 유동적인 형태로 데이터를 받고 싶을 때 사용한다. 

JSON의 키, 밸류가 Map형태로 담아진다. 

public class Bag {

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

    @JsonAnySetter
    public void set(String fieldName, Object value){
        this.properties.put(fieldName, value);
    }

    public Object get(String fieldName){
        return this.properties.get(fieldName);
    }
}

 

@JsonCreator

받아야 하는 JSON은 다음과 같다.

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

하지만 현재 사용하는 Entity와 매칭이 되지 않을 때에

아래의 방법은 유용하다. 

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

아래는 적용예제이다. 

@Test
public void whenDeserializingUsingJsonCreator_thenCorrect()
  throws IOException {
 
    String json = "{\"id\":1,\"theName\":\"My bean\"}";

    BeanWithCreator bean = new ObjectMapper()
      .readerFor(BeanWithCreator.class)
      .readValue(json);
    assertEquals("My bean", bean.name);
}

 

@JacksonInject

어떤 데이터는 JSON을 통해 받지 않고 별도로 주입해야 하는 경우가 있다.

public class BeanWithInject {
    @JacksonInject
    public int id;
    
    public String name;
}

테스트 코드 : 

@Test
public void whenDeserializingUsingJsonInject_thenCorrect()
  throws IOException {
 
    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);
    
    assertEquals("My bean", bean.name);
    assertEquals(1, bean.id);
}

 

@JsonDeserialize

먼저 @JsonDeserialize를 사용하여 CustomDateDeserializer로 eventDate 속성을 역직렬화한다.
다시말해, 
날짜 String의 형태로 직렬화된 상태에서

날짜 내부에서 쓸 수 있는 날짜 데이터로 변환되도록 역직렬화 작업을 수행한다. 

public class EventWithSerializer {
    public String name;

    @JsonDeserialize(using = CustomDateDeserializer.class)
    public Date eventDate;
}

사용자 지정 deserializer는 다음과 같다.

public class CustomDateDeserializer
  extends StdDeserializer<Date> {

    private static SimpleDateFormat formatter
      = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");

    public CustomDateDeserializer() { 
        this(null); 
    } 

    public CustomDateDeserializer(Class<?> vc) { 
        super(vc); 
    }

    @Override
    public Date deserialize(
      JsonParser jsonparser, 
      DeserializationContext context) throws IOException {
        String date = jsonparser.getText();
        try {
            return formatter.parse(date);
        } catch (ParseException e) {
            throw new RuntimeException(e);
        }
    }
}

테스트 코드

@Test
public void whenDeserializingUsingJsonDeserialize_thenCorrect()
  throws IOException {
 
    String json
      = "{"name":"party","eventDate":"20-12-2014 02:30:00"}";

    SimpleDateFormat df
      = new SimpleDateFormat("dd-MM-yyyy hh:mm:ss");
    EventWithSerializer event = new ObjectMapper()
      .readerFor(EventWithSerializer.class)
      .readValue(json);
    
    assertEquals(
      "20-12-2014 02:30:00", df.format(event.eventDate));
}

참고로, 직렬화방법에는 여러 Format이 존재한다.

 - 표형태의 다량의 데이터를 직렬화할 때는 CSV형태

 - 구조적인 데이터는 XML이나 JSON형태

 

 

Write Annotations

 

@JsonInclude

@JsonInclude  사용 하여 empty / null / 기본값이 있는 속성을 제외 할 수 있다.

아래는 null 대한 예제로 null은 제외한다. 

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

테스트 코드

public void whenSerializingUsingJsonInclude_thenCorrect()
  throws JsonProcessingException {
 
    MyBean bean = new MyBean(1, null);

    String result = new ObjectMapper()
      .writeValueAsString(bean);
    
    assertThat(result, containsString("1"));
    assertThat(result, not(containsString("name")));
}

@JsonInclude(JsonInclude.Include.ALWAYS) : 모든 값을 출력한다.

@JsonInclude(JsonInclude.Include.NON_EMPTY) :

  - Collection, Map의 isEmpty()가 true 이면 제외한다.

  - Array의 length가 0이면 제외한다.

  - String의 length()가 0이면 제외한다.

@JsonInclude(JsonInclude.Include.NON_DEFAULT) :

  -  empty는 제외된다. 

  - primitive 타입이 디폴트 값이면 제외한다. (int / Integer : 0 , boolean / Boolean : false 등)

  - Date의 timestamp가 0L이면 제외한다.

 

@JsonGetter

JSON의 키 값이 "theName"에서 "name"으로 지정해 준다.

getter 메서드명으로 키값을 정하지 않고 지정할 수 있다. 

@Builder
public static class MyBean {
    public int id;
    private String name;

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

 

@JsonValue

getName 에 @JsonValue 해당 멤버필드가 이름을 통해 직렬화 시킴

public class PersonValue {

    public long   personId = 0;
    public String name = null;

    @JsonValue
    public String toJson(){
        return this.personId + "," + this.name;
    }

}

직렬화 화면 이렇게 표출된다. 

"0,null"

 

@JsonRawValue

@JsonRawValue 는 Jackson이 속성을 그대로 직렬화하여 JSON으로 변경

@Builder
public static class RawBean {
	public String name;

	@JsonRawValue
	public String json;
}
//JSON
{
  "name": "등록",
  "json": "{\nstat\"\":false\n}"
}
//CONVERT
{
  "name": "등록",
  "json": {
    "stat": false
  }
}

 

 

참고

tutorials.jenkov.com/java-json/jackson-annotations.html

www.baeldung.com/jackson-annotations

반응형
Comments