1 분 소요

문제 상황

com.fasterxml.jackson.databind.JsonMappingException: Invalid white space character (0x8) in text to output (in xml 1.1, could output as a character entity), while writing Xml

객체를 xml 형식의 데이터로 처리가 필요해 XmlMapper를 사용하고 있는데, 위와 같은 에러가 발생했다.

대략 재현을 해보면 아래와 같다.

data class Person(val name: String)

@Test
fun `0x8 문자가 포함된 객체는 xml 파싱할  없다`() {
    val input = Person("John\u0008")
    assertThrows(JsonProcessingException::class.java) {
        XmlMapper().writeValueAsString(input) // JsonMappingException: Invalid white space character (0x3)
    }
}
  • 0x8 == /u0008
  • (참고) XmlMapper만 그렇고 ObjectMapper는 문제없음

Document authors are encouraged to avoid “compatibility characters”, as defined in Unicode. The characters defined in the following ranges are also discouraged. They are either control characters or permanently undefined Unicode characters:

[#x1-#x8], [#xB-#xC], [#xE-#x1F], [#x7F-#x84], [#x86-#x9F], [#xFDD0-#xFDDF],
[#x1FFFE-#x1FFFF], [#x2FFFE-#x2FFFF], [#x3FFFE-#x3FFFF],
[#x4FFFE-#x4FFFF], [#x5FFFE-#x5FFFF], [#x6FFFE-#x6FFFF],
[#x7FFFE-#x7FFFF], [#x8FFFE-#x8FFFF], [#x9FFFE-#x9FFFF],
[#xAFFFE-#xAFFFF], [#xBFFFE-#xBFFFF], [#xCFFFE-#xCFFFF],
[#xDFFFE-#xDFFFF], [#xEFFFE-#xEFFFF], [#xFFFFE-#xFFFFF],
[#x10FFFE-#x10FFFF].

찾아보니 xml에서 허용되지 않는 문자 리스트가 있었다.
위에 있는 것들을 compatibility character 라고 부르는 것 같다.

/u0008#x1-#x8에 속해서 invalid character로 분류된다.

해결 방법

replace

가장 간단한 방법이다. 일부 필드에만 invalid character가 들어온다면 효과적일 것 같다.

"John\u0008".replace("\u0008", "")

XmlMapper에 XmlSerializer 등록하기

\u0008을 빈 문자열로 replace 해주는 로직은 여기에서도 포함된다. 먼저 CustomXmlSerializer를 정의한다.

class CustomXmlSerializer : JsonSerializer<Any>() {
    override fun serialize(value: Any, gen: JsonGenerator, serializers: SerializerProvider) {
        val transformed = value.toString().replace("\u0008", "")
        gen.writeString(transformed)
    }
}


그리고 XmlMapper에 등록할 module에 CustomXmlSerializer를 적용한다.

val module = SimpleModule().addSerializer(String::class.java, CustomXmlSerializer())

XmlMapper()
    .registerModule(module)
    .writeValueAsString(Person("John\u0008"))

/u0008이 포함된 모든 문자열에 대해서 원천 차단은 가능하지만 항상 들어오지 않는 input에 대해서 처리를 해주는 건 낭비일 수 있다.

대신에 XmlMapper를 한 곳에서 생성해 관리한다면 관리 포인트도 한 곳으로 줄게 되는거라서, 수정이 간편하다는 장점이 있다.
상황에 맞게 적용하면 되겠다.

References

카테고리:

업데이트:

댓글남기기