DevLog ๐Ÿ˜ถ

[Spring] Jackson annotation - Deserialization ์•Œ์•„๋ณด๊ธฐ ๋ณธ๋ฌธ

Back-end/Spring

[Spring] Jackson annotation - Deserialization ์•Œ์•„๋ณด๊ธฐ

dolmeng2 2023. 4. 20. 18:54

์ง€๋‚œ ๋ฒˆ์— ์ง๋ ฌํ™” ๊ด€๋ จํ•ด์„œ ๊ธ€์„ ์ž‘์„ฑํ–ˆ์—ˆ์–ด์„œ, ์ด๋ฒˆ์—๋Š” Jackson์—์„œ ์ œ๊ณตํ•˜๋Š” ์—ญ์ง๋ ฌํ™” ๊ธฐ๋Šฅ์— ๋Œ€ํ•ด์„œ ๊ณต๋ถ€ํ•ด๋ณด๊ณ ์ž ํ•œ๋‹ค.


โœ”๏ธ Deserialization

Deserialization is the process of reconstructing a data structure or object from a series of bytes or a string in order to instantiate the object for consumption.

์—ญ์ง๋ ฌํ™”๋Š” data structure๋‚˜ ๊ฐ์ฒด๋ฅผ ์ผ๋ จ์˜ ๋ฐ”์ดํŠธ๋‚˜ ๋ฌธ์ž์—ด๋กœ ์žฌ๊ตฌ์„ฑํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋„๋ก ์ธ์Šคํ„ด์Šคํ™”ํ•˜๋Š” ํ”„๋กœ์„ธ์Šค์ด๋‹ค.

์ง๋ ฌํ™”์˜ ๋ฐ˜๋Œ€๋ผ๊ณ  ๋ณผ ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๋ณ€ํ™˜๋œ ๋ฐ์ดํ„ฐ๋ฅผ ์žฅ์น˜๊ฐ„์˜ ์ €์žฅ์ด๋‚˜ ์ „์†กํ•˜๋Š” ๊ฒƒ์ด๋‹ค.

https://hazelcast.com/glossary/deserialization/

 

Jackson์—์„œ๋Š” ์–ด๋–ค ์–ด๋…ธํ…Œ์ด์…˜์„ ํ†ตํ•ด ์—ญ์ง๋ ฌํ™”๋ฅผ ์ง€์›ํ•˜๋Š”์ง€ ํ™•์ธํ•ด๋ณด์ž.

 


๐ŸŒฑ Jackson์˜ ๊ธฐ๋ณธ ์—ญ์ง๋ ฌํ™” ์ „๋žต

์ง€๋‚œ ํฌ์ŠคํŒ…์—์„œ, ์ง๋ ฌํ™” ์‹œ getter๊ฐ€ ์—†์„ ๊ฒฝ์šฐ public ํ•„๋“œ์— ๋Œ€ํ•ด ์ง๋ ฌํ™”๋ฅผ ํ•˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ์—ˆ๋‹ค.

๊ทธ ์™ธ์˜ ํ•„๋“œ์— ๋Œ€ํ•ด์„œ๋Š” getter๊ฐ€ ์—ด๋ ค ์žˆ์–ด์•ผ ํ–ˆ๋Š”๋ฐ, ์—ญ์ง๋ ฌํ™” ์‹œ์—๋„ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ getter๊ฐ€ ์—ด๋ ค์žˆ์œผ๋ฉด ๋œ๋‹ค.

์ถ”๊ฐ€์ ์œผ๋กœ ์—ญ์ง๋ ฌํ™”์˜ ๊ฒฝ์šฐ setter๋ฅผ ํ†ตํ•ด์„œ๋„ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋Š”๋ฐ, ์—ฌ๋Ÿฌ ๊ฐ€์ง€ ์ผ€์ด์Šค๋ฅผ ํ…Œ์ŠคํŠธ ํ•ด๋ณด์ž.

 

๋„๋ฉ”์ธ ๊ตฌ์กฐ

public class Crew {

   public String publicName;
   protected String protectedName;
   String defaultName;
   private String privateName;
   
   ...
}

 

ํ™•์ธ ์ฝ”๋“œ

Crew crew = new Crew("์ ธ๋‹ˆ", "์ ธ๋‹ˆ", "์ ธ๋‹ˆ", "์ ธ๋‹ˆ");
String result = new ObjectMapper().writeValueAsString(crew);
System.out.println(result);

String json = "{\"publicName\":\"์ ธ๋‹ˆ\", \"protectedName\":\"์ ธ๋‹ˆ\", \"defaultName\":\"์ ธ๋‹ˆ\", \"privateName\":\"์ ธ๋‹ˆ\"}";
Crew crew2 = new ObjectMapper().readerFor(Crew.class).readValue(json);
System.out.println(crew2);

 

1) Getter : ์ง๋ ฌํ™” O, ์—ญ์ง๋ ฌํ™” X

 

2) Getter + ๊ธฐ๋ณธ ์ƒ์„ฑ์ž : ์ง๋ ฌํ™” O, ์—ญ์ง๋ ฌํ™” O

 

3) Setter : public ํ•„๋“œ๋งŒ ์ง๋ ฌํ™” O, ์—ญ์ง๋ ฌํ™” X

 

4) Setter + ๊ธฐ๋ณธ ์ƒ์„ฑ์ž : public ํ•„๋“œ๋งŒ ์ง๋ ฌํ™” O, ์—ญ์ง๋ ฌํ™” O

 

๊ฒฐ๊ณผ๋ฅผ ๋ณด๋ฉด getter๋Š” ์ง๋ ฌํ™” ์ „์šฉ, ๊ธฐ๋ณธ ์ƒ์„ฑ์ž + setter๋Š” ์—ญ์ง๋ ฌํ™” ๋Š๋‚Œ์ด๋‹ค.

๊ธฐ๋ณธ ์ƒ์„ฑ์ž๋ฅผ ์„ ์–ธํ•˜๊ณ  ๋ฆฌํ”Œ๋ ‰์…˜์„ ํ†ตํ•ด์„œ ๊ฐ์ฒด์˜ ๊ฐ’์„ ์ฑ„์›Œ์ฃผ๋Š” ๊ฒƒ์ด๋ผ๊ณ  ์ถ”์ •๋œ๋‹ค.

 

์ •๋ง ๊ฐ„๋žตํ•˜๊ฒŒ ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•ด๋ดค๋Š”๋ฐ, ์•„๋ž˜์™€ ๊ฐ™์ด ๊ธฐ๋ณธ ์ƒ์„ฑ์ž๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๋Š” ์ฝ”๋“œ๊ฐ€ ์žˆ๋‹ค.

๊ธฐ๋ณธ ์ƒ์„ฑ์ž๋ฅผ ์ƒ์„ฑํ•˜๊ธฐ.

call()์ด๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค.

๊ทธ๋ฆฌ๊ณ  ์—ฌ๊ธฐ์„œ newInstance๋ฅผ ํ†ตํ•ด์„œ ์ƒˆ๋กœ์šด ๊ฐ์ฒด๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค.

์•„๊นŒ ์œ„์˜ ์ฝ”๋“œ(vanillaDeserialize)์—์„œ beanProperties.find()๋ผ๋Š” ๋ฉ”์„œ๋“œ๊ฐ€ ์žˆ์—ˆ๋Š”๋ฐ, ๊ฑฐ๊ธฐ์„œ deserializeAndSet()์ด๋ผ๋Š” ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•œ๋‹ค. ๊ทธ๋ฆฌ๊ณ  valueDeserializer.deserialize() ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•˜๋Š”๋ฐ, ์•„๋งˆ ์ €๊ธฐ์„œ... ๋ฆฌํ”Œ๋ ‰์…˜์„ ์“ฐ๋Š” ๊ฒƒ ๊ฐ™๋‹ค ใ…Žใ…Ž 

 

์•„๋ฌดํŠผ! ์ •๋ฆฌํ•˜์ž๋ฉด ์•„๋ž˜์™€ ๊ฐ™์ด ๋‚˜ํƒ€๋‚ผ ์ˆ˜ ์žˆ๋‹ค.

ํƒ€์ž… ์ง๋ ฌํ™” ๊ฐ€๋Šฅ ์—ฌ๋ถ€ ์—ญ์ง๋ ฌํ™” ๊ฐ€๋Šฅ ์—ฌ๋ถ€
Getter O X
Getter + ๊ธฐ๋ณธ ์ƒ์„ฑ์ž O O
Setter  public ํ•„๋“œ๋งŒ  X
Setter + ๊ธฐ๋ณธ ์ƒ์„ฑ์ž public ํ•„๋“œ๋งŒ O

 


๐ŸŒฑ @JsonSetter

Annotation that can be used to define a non-static, single-argument method to be used as a "setter" for a logical property as an alternative to recommended JsonProperty annotation; or (as of 2.9 and later), specify additional aspects of the assigning property a value during serialization.
JsonProperty ์–ด๋…ธํ…Œ์ด์…˜์„ ๋Œ€์ฒดํ•˜์—ฌ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.
non-static์ด๋ฉฐ 1๊ฐœ์˜ ์ธ์ž๋ฅผ ๊ฐ€์ง„ ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•ด์„œ ์—ญ์ง๋ ฌํ™” ์‹œ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ค.

 

๋ฐ”์ธ๋”ฉํ•  ๊ฐ์ฒด์™€ json์˜ ํ•„๋“œ ์ด๋ฆ„์ด ์ผ์น˜ํ•˜์ง€ ์•Š์„ ๋•Œ ์œ ์šฉํ•˜๊ฒŒ ์‚ฌ์šฉํ•œ๋‹ค.

@JsonGetter์™€ ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ non-static ํ•ด์•ผ ํ•˜๋ฉฐ, ์˜ค์ง 1๊ฐœ์˜ argument๋งŒ ๋ฐ›๋Š”๋‹ค.

{ 
	"name": "์ ธ๋‹ˆ", 
	"age": 23,
        "menus": [
            {"name":"๋งˆ๋ผํƒ•", "price": 10000},
            {"name":"์ฐœ๋‹ญ", "price": 15000}
        ]
}

์œ„์™€ ๊ฐ™์€ Json์„ ๊ฐ์ฒด๋กœ ๋ณ€๊ฒฝํ•ด๋ณธ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด์ž.

์ด๋•Œ ์‹ค์ œ ๊ฐ์ฒด์—์„œ๋Š” menus์— ๊ด€ํ•œ ํ•„๋“œ๊ฐ€ ์—†๊ณ , lunchMenus๋ผ๋Š” ํ•„๋“œ๊ฐ€ ์žˆ์–ด์„œ ๊ทธ์ชฝ์œผ๋กœ ๋งคํ•‘์„ ์‹œํ‚ค๋ ค๊ณ  ํ•œ๋‹ค.

 

public class Menu {
    private String name;
    private int price;

    public Menu() {
    }

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

    public void setPrice(final int price) {
        this.price = price;
    }
    
    @Override
    public String toString() {
        return "Menu{" +
                "name='" + name + '\'' +
                ", price=" + price +
                '}';
    }
}
public class Crew {
    private String name;
    private int age;
    private List<Menu> lunchMenus;

    public Crew() {
    }

    @JsonSetter("menus")
    public void setLunchMenus(final List<Menu> lunchMenus) {
        this.lunchMenus = lunchMenus;
    }

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

    public void setAge(final int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Crew{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", lunchMenus=" + lunchMenus +
                '}';
    }
}

์œ„์™€ ๊ฐ™์ด @JsonSetter๋ฅผ ํ†ตํ•ด ์–ด๋–ค ํ•„๋“œ์˜ ๊ฐ’์ด ์ฃผ์ž…๋ ์ง€ ์„ ํƒํ•  ์ˆ˜ ์žˆ๋‹ค.

์‹ค์ œ๋กœ ์ถœ๋ ฅ์„ ํ•ด์„œ ํ™•์ธ์„ ํ•ด๋ณด์•˜๋‹ค.

String json = "{\"name\": \"์ ธ๋‹ˆ\", " +
            "\"age\": 23, " +
            "\"menus\": [" +
            "{\"name\":\"๋งˆ๋ผํƒ•\", \"price\": 10000}, " +
            "{\"name\":\"์ฐœ๋‹ญ\", \"price\": 15000}]} ";

Crew crew = new ObjectMapper().readerFor(Crew.class).readValue(json);

์‹ค์ œ๋กœ ์ถœ๋ ฅ์„ ํ•ด๋ณด๋ฉด ์œ„์™€ ๊ฐ™์ด ๋ชจ๋‘ lunchMenu๋ผ๋Š” ํ•„๋“œ์— ์ž˜ ๋“ค์–ด๊ฐ„ ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 


๐ŸŒฑ @JsonAnySetter

Marker annotation that can be used to define a logical "any setter" mutator -- either using non-static two-argument method (first argument name of property, second value to set) or a field (of type Map or POJO) - to be used as a "fallback" handler for all otherwise unrecognized properties found from JSON content. It is similar to XmlAnyElement in behavior; and can only be used to denote a single property per type.
If used, all otherwise unmapped key-value pairs from JSON Object values are added using mutator.
non-static, 2๊ฐœ์˜ ์ธ์ž๋ฅผ ๊ฐ€์ง„ ๋ฉ”์„œ๋“œ๋‚˜ (์ฒซ ๋ฒˆ์งธ๋Š” ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„, ๋‘ ๋ฒˆ์งธ๋Š” ์„ค์ •ํ•œ ๊ฐ’) ํ˜น์€ Map์„ ์ธ์ž๋กœ ๊ฐ€์ง€๊ณ  ์žˆ๋Š” ๋ฉ”์„œ๋“œ์— ๋Œ€ํ•ด ์—ญ์ง๋ ฌํ™”๋ฅผ ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.
Json ๊ฐ์ฒด ๊ฐ’์—์„œ ๋งคํ•‘๋˜์ง€ ์•Š์€ ๋ชจ๋“  ํ‚ค-๊ฐ’ ์Œ์ด mutator๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์ถ”๊ฐ€๋  ์ˆ˜ ์žˆ๋„๋ก ํ•œ๋‹ค.

 

Map์„ ์ด์šฉํ•ด ์—ญ์ง๋ ฌํ™”๋ฅผ ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด๋‹ค.

๋งˆ์ฐฌ๊ฐ€์ง€๋กœ non-staticํ•œ ๋ฉ”์„œ๋“œ์—ฌ์•ผ ํ•˜๋ฉฐ, ์ฒซ ๋ฒˆ์งธ ์ธ์ž๋Š” ํ‚ค, ๋‘ ๋ฒˆ์งธ ์ธ์ž๋กœ value๋ฅผ ๋ฐ›๋Š”๋‹ค.

public class Wootecho {
    private Map<String, String> crews = new HashMap<>();

    public Map<String, String> getCrews() {
        return crews;
    }

    @JsonAnySetter
    public void addCrew(final String name, final String course) {
        crews.put(name, course);
    }
}

ํฌ๋ฃจ๋“ค์˜ ๋ฆฌ์ŠคํŠธ๋ฅผ ๋‹ด๋Š” ์šฐํ…Œ์ฝ” ํด๋ž˜์Šค๊ฐ€ ์žˆ๋‹ค๊ณ  ์ƒ๊ฐํ•ด๋ณด์ž.

addCrew๋ฅผ ํ†ตํ•ด ์ด๋ฆ„๊ณผ ํ•ด๋‹น ํฌ๋ฃจ์˜ ๊ณผ์ • ์ •๋ณด๋ฅผ ๋ฐ›์•„์„œ map์— ์ถ”๊ฐ€ํ•ด์ฃผ๋Š” ์ฝ”๋“œ์ด๋‹ค.

๊ทธ๋ฆฌ๊ณ , ์—ฌ๊ธฐ์— @JsonAnySetter๋ฅผ ํ™œ์šฉํ•˜๋ฉด ์—ญ์ง๋ ฌํ™” ๊ณผ์ •์—์„œ json ํ”„๋กœํผํ‹ฐ์˜ ๊ฐ’์„ ์‰ฝ๊ฒŒ map์— ๋‹ด์„ ์ˆ˜ ์žˆ๋‹ค.

 

String json = "{" +
            "\"ํฌ๋ฃจ1\":\"๋ฐฑ์—”๋“œ\"," +
            "\"ํฌ๋ฃจ2\":\"ํ”„๋ก ํŠธ์—”๋“œ\"," +
            "\"ํฌ๋ฃจ3\":\"์•ˆ๋“œ๋กœ์ด๋“œ\"" +
            "}";

Wootecho wootecho = new ObjectMapper().readerFor(Wootecho.class).readValue(json);
Map<String, String> crews = wootecho.getCrews();
System.out.println(crews);

๋‹ค์Œ๊ณผ ๊ฐ™์€ ์ฝ”๋“œ๋ฅผ ์ž‘์„ฑํ•˜๊ณ  ์‹คํ–‰ํ•ด๋ณด์ž.

๊ทธ๋Ÿผ ์œ„์ฒ˜๋Ÿผ ๋งต์— ํฌ๋ฃจ๋“ค์˜ ์ •๋ณด๊ฐ€ ์ž˜ ๋‹ด๊ธด ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 


 

๐ŸŒฑ @JsonCreator

Marker annotation that can be used to define constructors and factory methods as one to use for instantiating new instances of the associated class.
์ƒ์„ฑ์ž๋‚˜ ์ •์  ํŒฉํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ๋ฅผ ์—ฐ๊ด€๋œ ํด๋ž˜์Šค์˜ ์ƒˆ๋กœ์šด ์ธ์Šคํ„ด์Šค๋ฅผ ์ธ์Šคํ„ด์Šคํ™”ํ•  ๋•Œ ์‚ฌ์šฉํ•˜๋Š” ๋งˆ์ปค ์–ด๋…ธํ…Œ์ด์…˜์ด๋‹ค.
NOTE: when annotating creator methods (constructors, factory methods), method must either be:
- Single-argument constructor/factory method without JsonProperty annotation for the argument: if so, this is so-called "delegate creator", in which case Jackson first binds JSON into type of the argument, and then calls creator. This is often used in conjunction with JsonValue (used for serialization).
- Constructor/factory method where every argument is annotated with either JsonProperty or JacksonInject, to indicate name of property to bind to
์ฐธ๊ณ ๋กœ, ์ƒ์„ฑ์ž ๋ฉ”์„œ๋“œ์— ์–ด๋…ธํ…Œ์ด์…˜์„ ๋‹ฌ๊ธฐ ์œ„ํ•ด์„œ๋Š” ํ•ด๋‹น ๋ฉ”์„œ๋“œ๊ฐ€ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ์‚ฌํ•ญ ์ค‘ ํ•˜๋‚˜์—ฌ์•ผ ํ•œ๋‹ค.
- ์ธ์ž์— ๋Œ€ํ•ด @JsonProperty๊ฐ€ ์—†๋Š” ๋‹จ์ผ ์ธ์ž ์ƒ์„ฑ์ž, ํ˜น์€ ํŒฉํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ (๋ธ๋ฆฌ๊ฒŒ์ดํŠธ ์ƒ์„ฑ์ž๋ผ๊ณ  ํ•œ๋‹ค)
: ์ด๋•Œ Jackson์€ Json์„ ๋จผ์ € argument์˜ ํƒ€์ž…์— ๋ฐ”์ธ๋”ฉํ•˜๊ณ  ์ƒ์„ฑ์ž๋ฅผ ํ˜ธ์ถœํ•˜๋ฉฐ, jsonValue์™€ ์ž์ฃผ ์‚ฌ์šฉํ•œ๋‹ค.
- ๋ฐ”์ธ๋”ฉํ•  ํ”„๋กœํผํ‹ฐ ์ด๋ฆ„์„ ๋‚˜ํƒ€๋‚ด๊ธฐ ์œ„ํ•ด, ๋ชจ๋“  argument๋Š” jsonProperty ๋˜๋Š” JacksonInject๋กœ ์ง€์ •๋œ ์ƒ์„ฑ์ž๋‚˜ ํŒฉํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ์—ฌ์•ผ ํ•œ๋‹ค.
Also note that all JsonProperty annotations must specify actual name (NOT empty String for "default") unless you use one of extension modules that can detect parameter name; this because default JDK versions before 8 have not been able to store and/or retrieve parameter names from bytecode. 

One common use case is to use a delegating Creator to construct instances from scalar values (like java.lang.String) during deserialization, and serialize values using JsonValue.
๋˜ํ•œ, ๋งค๊ฐœ๋ณ€์ˆ˜ ์ด๋ฆ„์„ ๊ฐ์ง€ํ•  ์ˆ˜ ์žˆ๋Š” ํ™•์žฅ ๋ชจ๋“ˆ์„ ์‚ฌ์šฉํ•˜์ง€ ์•Š๋Š” ํ•œ, ๋ชจ๋“  @JsonProperty ์–ด๋…ธํ…Œ์ด์…˜์€ ์‹ค์ œ ์ด๋ฆ„ (๋นˆ ๋ฌธ์ž์—ด์ด ์•„๋‹Œ)์„ ์ง€์ •ํ•ด์•ผ ํ•˜๋Š”๋ฐ, JDK 1.8 ์ด์ „์—๋Š” ๋ฐ”์ดํŠธ์ฝ”๋“œ์—์„œ ๋งค๊ฐœ๋ณ€์ˆ˜ ์ด๋ฆ„์„ ์ €์žฅ ๋ฐ ๊ฒ€์ƒ‰์ด ๋˜์ง€ ์•Š์•˜์—ˆ๋‹ค. (ํ•˜์ง€๋งŒ ๊ทธ ์ดํ›„๋ถ€ํ„ฐ๋Š” ๊ฐ€๋Šฅํ•˜๊ฒŒ ๋˜๋ฉด์„œ ์ด๋ฆ„ ์ง€์ •์ด ์„ ํƒ ์‚ฌํ•ญ์ด ๋˜์—ˆ๋‹ค๊ณ  ํ•œ๋‹ค.)
์ผ๋ฐ˜์ ์œผ๋กœ๋Š” ์—ญ์ง๋ ฌํ™” ์ค‘์— delegating Creator๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ String ๊ฐ™์€ ์Šค์นผ๋ผ ๊ฐ’์œผ๋กœ ์ธ์Šคํ„ด์Šค๋ฅผ ๊ตฌ์„ฑํ•˜๊ณ , jsonValue๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฐ’์„ ์ง๋ ฌํ™”ํ•  ๋•Œ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค.

 

์ƒ์„ฑ์ž๋‚˜ ์ •์  ํŒฉํ„ฐ๋ฆฌ ๋ฉ”์„œ๋“œ์—์„œ ์ฃผ๋กœ ์‚ฌ์šฉํ•˜๋ฉฐ, ์ƒ์„ฑ์ž์˜ ํ•„๋“œ ์ด๋ฆ„์ด json์˜ ํ•„๋“œ๊ฐ’๊ณผ ๋‹ค๋ฅผ ๋•Œ ์ฃผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค.

๋ณดํ†ต @JsonProperty์™€ ์กฐํ•ฉํ•˜์—ฌ ๋งŽ์ด ์‚ฌ์šฉํ•œ๋‹ค.

public class Crew {
    private String name;
    private int age;

    @JsonCreator
    public Crew(
            @JsonProperty("crewName") final String name,
            @JsonProperty("crewAge") final int age) {
        this.name = name;
        this.age = age;
    }

    @Override
    public String toString() {
        return "Crew{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

์œ„์™€ ๊ฐ™์€ ํฌ๋ฃจ ํด๋ž˜์Šค๊ฐ€ ์žˆ๋‹ค๊ณ  ํ–ˆ์„ ๋•Œ, ํ•ด๋‹น ํฌ๋ฃจ ํด๋ž˜์Šค์˜ ์ƒ์„ฑ์ž์— @JsonCreator๋ฅผ ๋ถ™์—ฌ์ฃผ๊ณ , ์ƒ์„ฑ์ž์˜ ์ธ์ž์— @JsonProperty๋ฅผ ๋ถ™์—ฌ์ค€๋‹ค. ์ด๋Ÿฌ๋ฉด ์ƒ์„ฑ์ž์˜ ์ธ์ž๋กœ ์—ญ์ง๋ ฌํ™” ์‹œ json ๊ฐ’์ด ๋งคํ•‘๋œ๋‹ค.

@Test
void test() throws JsonProcessingException {
    // given
    String json = "{\"crewName\": \"์ ธ๋‹ˆ\", " +
            "\"crewAge\": 23} ";

    // when
    Crew crew = new ObjectMapper().readerFor(Crew.class).readValue(json);
    System.out.println(crew);
}

์‹ค์ œ๋กœ ์ฝ”๋“œ๋ฅผ ๋Œ๋ ค๋ณด๋ฉด ๊ต‰์žฅํžˆ ์ž˜ ๋งคํ•‘๋˜๋Š” ๊ฒƒ์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.

 

๋‚ด๋ถ€์˜ ์ธ์ž๊ฐ€ ๋ฆฌ์ŠคํŠธ์ผ ๋•Œ๋„ ์ž˜ ๋งคํ•‘๋˜๋Š”์ง€ ๊ถ๊ธˆํ•ด์„œ ํ•ด๋ดค๋‹ค.

public class Crew {
    private String name;
    private int age;
    private List<Menu> lunchMenus;

    @JsonCreator
    public Crew(@JsonProperty("crewName") final String name,
                @JsonProperty("crewAge") final int age,
                @JsonProperty("menus") final List<Menu> lunchMenus) {
        this.name = name;
        this.age = age;
        this.lunchMenus = lunchMenus;
    }
    ...
}

๊ต‰์žฅํžˆ ๋งคํ•‘์ด ์ž˜ ๋˜๋Š” ๋ชจ์Šต์„ ๋ณผ ์ˆ˜ ์žˆ๋‹ค.


๐ŸŒฑ @JacksonInject

Jackson-specific annotation used for indicating that value of annotated property will be "injected", i.e. set based on value configured by ObjectMapper (usually on per-call basis). Usually property is not deserialized from JSON, although it possible to have injected value as default and still allow optional override from JSON.
์–ด๋…ธํ…Œ์ด์…˜์ด ์ง€์ •๋œ ํ”„๋กœํผํ‹ฐ์˜ ๊ฐ’์ด ์ฃผ์ž…๋  ๊ฒƒ์ด๋ผ๊ณ  ์‚ฌ์šฉ๋˜๋Š” Jackson ์ „์šฉ ์–ด๋…ธํ…Œ์ด์…˜์ด๋‹ค.
ObjectMapper๊ฐ€ ๊ตฌ์„ฑํ•œ ๊ฐ’์— ๋”ฐ๋ผ์„œ ์„ค์ •๋˜๋ฉฐ, ์ผ๋ฐ˜์ ์œผ๋กœ ํ”„๋กœํผํ‹ฐ๋Š” Json์œผ๋กœ๋ถ€ํ„ฐ ์—ญ์ง๋ ฌํ™”๋˜์ง€ ์•Š์ง€๋งŒ, ๊ธฐ๋ณธ๊ฐ’์œผ๋กœ inject๋œ ๊ฐ’์„ ์‚ฌ์šฉํ•˜์—ฌ Json์—์„œ ์„ ํƒ์ ์œผ๋กœ ์žฌ์ •์˜๊ฐ€ ๊ฐ€๋Šฅํ•˜๋‹ค.

 

@JacksonInject๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด Json ๋ฐ์ดํ„ฐ์˜ ํ•„๋“œ๊ฐ’์ด ์•„๋‹Œ ๊ฐ’์— ๋Œ€ํ•ด์„œ๋„ inject๋ฅผ ์‹œํ‚ฌ ์ˆ˜ ์žˆ๋‹ค. 

์ด๊ฑด ์ฝ”๋“œ๋กœ ๋ณด๋Š” ๊ฒŒ ๋” ์ดํ•ดํ•˜๊ธฐ ์‰ฝ๊ธฐ ๋•Œ๋ฌธ์— ์ฝ”๋“œ๋ฅผ ํ™•์ธํ•ด๋ณด์ž.

public class Crew {
    @JacksonInject
    private Long id;
    private String name;
    private int age;

    public Crew() {
    }

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

    public void setAge(final int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "Crew{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

์œ„์™€ ๊ฐ™์ด id๋ผ๋Š” ํ•„๋“œ๋ฅผ ์ถ”๊ฐ€ํ•ด์ฃผ์—ˆ๋‹ค. ์ด ๊ฐ’์€ json์œผ๋กœ ๋“ค์–ด์˜ค์ง€ ์•Š์„ ๊ฐ’์ด๋‹ค.

String json = "{\"name\": \"์ ธ๋‹ˆ\", " +
                "\"age\": 23} ";

InjectableValues inject = new InjectableValues.Std()
        .addValue(Long.class, 1L);

Crew crew = new ObjectMapper().reader(inject)
        .forType(Crew.class)
        .readValue(json);
System.out.println(crew);

๊ทธ๋ฆฌ๊ณ , ์œ„์™€ ๊ฐ™์ด InjectableValues๋ฅผ ํ™œ์šฉํ•˜์—ฌ์„œ ๊ฐ’์„ ์ฃผ์ž…ํ•ด์ฃผ์—ˆ๋‹ค.

๊ทธ๋Ÿผ ์ด๋Ÿฐ ์‹์œผ๋กœ 1์ด ๋“ค์–ด๊ฐ„๋‹ค! (์‹ ๊ธฐํ•˜๋‹ค)

์–ด๋–ค ์ƒํ™ฉ์—์„œ ์‘์šฉํ•  ์ˆ˜ ์žˆ์„์ง€ ์ƒ๊ฐํ•ด๋ดค๋Š”๋ฐ, ์š”์ฒญ์ด ๋“ค์–ด์˜จ ์‹œ๊ฐ„์— ๋Œ€ํ•ด ํ•„๋“œ์— ์ €์žฅํ•  ๋•Œ ์‚ฌ์šฉํ•  ์ˆ˜๋„ ์žˆ์„ ๊ฒƒ ๊ฐ™๊ณ  (๋ฌผ๋ก  ๊ทธ๋ƒฅ LocalDateTime์„ ์“ธ ์ˆ˜๋„ ์žˆ๊ฒ ์ง€๋งŒ?) DB ํ•„๋“œ์˜ ๋งˆ์ง€๋ง‰ ์—…๋ฐ์ดํŠธ ์‹œ๊ฐ„ ๋“ฑ์œผ๋กœ ํ™œ์šฉํ•  ์ˆ˜ ์žˆ์„ ๊ฒƒ ๊ฐ™๋‹ค. (๋ฌผ๋ก  ์ด๊ฒƒ๋„ JpaAuditing ์‚ฌ์šฉํ•˜๋ฉด ๋˜๊ธด ํ•˜๊ฒ ์ง€๋งŒ...)

 

์•„๋ฌดํŠผ ์—ญ์ง๋ ฌํ™”์— ๋Œ€ํ•ด์„œ ์•Œ์•„๋ณด์•˜๋‹ค. ๋‹ค์Œ์—๋Š” ObjectMapper์˜ ๋™์ž‘์›๋ฆฌ๋„ ์ข€ ํ™•์ธํ•ด๋ณผ ์˜ˆ์ •์ด๋‹ค.

Comments