[Jackson] invalid white space character (0x8) in text to output
문제 상황
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].
- w3.org 참조
찾아보니 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
를 한 곳에서 생성해 관리한다면 관리 포인트도 한 곳으로 줄게 되는거라서, 수정이 간편하다는 장점이 있다.
상황에 맞게 적용하면 되겠다.
댓글남기기