티스토리 뷰

Spring

Json 맵핑 원리

gajy 2022. 4. 2. 00:42
728x90

 

@ReqeustBody나 Jackson을 이용해 Object를 맵핑할 때 어노테이션을 쓰는 경우도 있고, getter/setter로 맵핑되게 구성하는 경우도 있다.

 

맵핑을 가능하게 하는 케이스들을 알아보자

 

1. 어노테이션 (getter/setter 없이)

@JsonProperty, @JsonAutoDetect 사용

 

2. ObjectMapper 설정

ObjectMapper objectMapper = new ObjectMapper();
objectMapper.setVisibility(PropertyAccessor.FIELD, JsonAutoDetect.Visibility.ANY);

 

3. getter/setter 설정

하지만 이 케이스들을 중복해서 사용할 경우 데이터가 여러개 들어갈 수 있으니 조심해야한다.

예를들면 @JsonProperty를 설정하고, getter/setter를 설정하면 값이 중복해 들어간다.

 

 


 

ObjectMapper readValue(역직렬화 JSON -> Object) method를 통해 분석해보았다. (jackson 2.7.3 기준)

 

1. getter / setter는 어떻게 직렬화, 역직렬화를 할까?

직렬화: getter, 역직렬화: setter를 사용한다.

그런데 사실 setter없이 getter만으로도 직렬화, 역직렬화가 가능하다.

 

2. setter 없이 값을 어떻게 주입할까?

소스코드를 보면 java.lang.reflect 패키지를 통해 직접 주입한다.

public final class FieldProperty
    extends SettableBeanProperty
{
    ...

    /**
     * Actual field to set when deserializing this property.
     * Transient since there is no need to persist; only needed during
     * construction of objects.
     */
    final protected transient Field _field;

    @Override
    public final void set(Object instance, Object value) throws IOException
    {
        try {
            _field.set(instance, value);
        } catch (Exception e) {
            // 15-Sep-2015, tatu: How coud we get a ref to JsonParser?
            _throwAsIOE(e, value);
        }
    }
}

 

3. 직렬화는?

직렬화는 writeValue method를 통해 알아 볼 수 있다.

 

 

4. 어떻게 ObjectMapper를 기반으로 @ReqeustBody Object로 맵핑이 연결될까?

Spring에서 HttpMessageConverter가 이 역할을 해주고, 나는 converter 중 MappingJackson2HttpMessageConvertert를 사용하였다.

dispatcher-servlet.xml에서 설정 가능하다.

[dispatcher-servlet.xml]

<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">

소스를 보면 ObjectMapper를 사용하고있다.

public class MappingJackson2HttpMessageConverter extends AbstractJackson2HttpMessageConverter {
    private String jsonPrefix;

    public MappingJackson2HttpMessageConverter() {
        this(Jackson2ObjectMapperBuilder.json().build());
    }

    public MappingJackson2HttpMessageConverter(ObjectMapper objectMapper) {
        super(objectMapper, new MediaType[]{MediaType.APPLICATION_JSON_UTF8, new MediaType("application", "*+json", DEFAULT_CHARSET)});
    }

    public void setJsonPrefix(String jsonPrefix) {
        this.jsonPrefix = jsonPrefix;
    }

    public void setPrefixJson(boolean prefixJson) {
        this.jsonPrefix = prefixJson ? ")]}', " : null;
    }

    protected void writePrefix(JsonGenerator generator, Object object) throws IOException {
        if (this.jsonPrefix != null) {
            generator.writeRaw(this.jsonPrefix);
        }

        String jsonpFunction = object instanceof MappingJacksonValue ? ((MappingJacksonValue)object).getJsonpFunction() : null;
        if (jsonpFunction != null) {
            generator.writeRaw("/**/");
            generator.writeRaw(jsonpFunction + "(");
        }

    }

    protected void writeSuffix(JsonGenerator generator, Object object) throws IOException {
        String jsonpFunction = object instanceof MappingJacksonValue ? ((MappingJacksonValue)object).getJsonpFunction() : null;
        if (jsonpFunction != null) {
            generator.writeRaw(");");
        }

    }
}

AbstractJackson2HttpMessageConverter 소스를 분석해보면 objectMapper의 readValue method를 이용한다는 것을 알 수 있다.

@Override
	public Object read(Type type, Class<?> contextClass, HttpInputMessage inputMessage)
			throws IOException, HttpMessageNotReadableException {

		JavaType javaType = getJavaType(type, contextClass);
		return readJavaType(javaType, inputMessage);
	}

	@SuppressWarnings("deprecation")
	private Object readJavaType(JavaType javaType, HttpInputMessage inputMessage) {
		try {
			if (inputMessage instanceof MappingJacksonInputMessage) {
				Class<?> deserializationView = ((MappingJacksonInputMessage) inputMessage).getDeserializationView();
				if (deserializationView != null) {
					return this.objectMapper.readerWithView(deserializationView).withType(javaType).
							readValue(inputMessage.getBody());
				}
			}
			return this.objectMapper.readValue(inputMessage.getBody(), javaType);
		}
		catch (IOException ex) {
			throw new HttpMessageNotReadableException("Could not read document: " + ex.getMessage(), ex);
		}
	}
728x90
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/10   »
1 2 3 4 5
6 7 8 9 10 11 12
13 14 15 16 17 18 19
20 21 22 23 24 25 26
27 28 29 30 31