Kotlin使用JacksonXML反序列化Qt TS文件偶现字段报错如何解决
问题原因
你当前的崩溃源于Jackson XML反序列化的两个冲突点:
Translation类同时绑定了标签内纯文本(@JacksonXmlText)和子节点numerusform,当<translation>标签内只有纯文本没有子节点时,Jackson默认的反序列化逻辑会认为结构不匹配,抛出解析异常。- 没有配置Jackson忽略未知属性/兼容非标准结构,只要遇到和定义不完全匹配的节点就会直接终止解析。
解决方案
第一步:调整数据类注解配置
给所有数据类加上忽略未知属性的注解,同时修正Translation类的冗余注解,删除不必要的自定义setter逻辑:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties import com.fasterxml.jackson.dataformat.xml.annotation.* @JsonIgnoreProperties(ignoreUnknown = true) data class Context( @JacksonXmlProperty(localName = "name") val name: String = "", @JacksonXmlElementWrapper(useWrapping = false) @JacksonXmlProperty(localName = "message") val messages: List<Message> = emptyList() ) @JsonIgnoreProperties(ignoreUnknown = true) data class Message( @JacksonXmlProperty(localName = "numerus", isAttribute = true) val numerus: String? = null, @JacksonXmlProperty(localName = "source") val source: String? = null, @JacksonXmlProperty(localName = "comment") val comment: String? = null, @JacksonXmlProperty(localName = "translation") val translation: Translation? = null ) @JsonIgnoreProperties(ignoreUnknown = true) data class Translation( @JacksonXmlProperty(localName = "type", isAttribute = true) val type: String? = null, @JacksonXmlText val text: String? = null, @JacksonXmlElementWrapper(useWrapping = false) @JacksonXmlProperty(localName = "numerusform") val numerusForm: List<NumerusForm> = emptyList() ) @JsonIgnoreProperties(ignoreUnknown = true) data class NumerusForm( @JacksonXmlText val text: String? = null )
Jackson本身支持直接注入不可变List集合,你原来的私有中间列表+自定义setter逻辑反而容易引发注入异常。
第二步:配置XmlMapper兼容混合内容
初始化XmlMapper的时候开启兼容配置,避免结构不匹配直接崩溃:
import com.fasterxml.jackson.databind.DeserializationFeature import com.fasterxml.jackson.dataformat.xml.XmlMapper import com.fasterxml.jackson.dataformat.xml.deser.FromXmlParser import com.fasterxml.jackson.module.kotlin.registerKotlinModule val xmlMapper = XmlMapper().apply { // 注册Kotlin模块,支持data class的无默认构造函数反序列化 registerKotlinModule() // 遇到未知属性不抛出异常,直接忽略 configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false) // 允许将标签内的纯文本直接解析为@JacksonXmlText标注的字段 configure(FromXmlParser.Feature.PARSE_TEXT_AS_VALUE, true) // 空标签解析为null而非空对象 configure(FromXmlParser.Feature.EMPTY_ELEMENT_AS_NULL, true) // 允许空字符串解析为null对象 configure(DeserializationFeature.ACCEPT_EMPTY_STRING_AS_NULL_OBJECT, true) }
第三步(可选兜底方案):自定义Translation反序列化器
如果上面的配置还是不能兼容极端场景,可以手写自定义反序列化器手动处理两种结构:
import com.fasterxml.jackson.core.JsonParser import com.fasterxml.jackson.databind.DeserializationContext import com.fasterxml.jackson.databind.JsonNode import com.fasterxml.jackson.databind.deser.std.StdDeserializer import com.fasterxml.jackson.databind.annotation.JsonDeserialize class TranslationDeserializer : StdDeserializer<Translation>(Translation::class.java) { override fun deserialize(p: JsonParser, ctxt: DeserializationContext): Translation { val node = p.codec.readTree<JsonNode>(p) // 读取type属性 val type = node.get("type")?.asText() // 读取纯文本内容 val text = if (node.isTextual) node.asText() else node.get("")?.asText() // 读取numerusform子节点 val numerusForms = node.findValues("numerusform").map { NumerusForm(it.asText()) } return Translation(type, text, numerusForms) } }
然后给Translation类加上注解指定反序列化器即可:
@JsonDeserialize(using = TranslationDeserializer::class) @JsonIgnoreProperties(ignoreUnknown = true) data class Translation( // 字段定义保持不变 )
效果验证
调整后两种结构都可以正常解析:
- 普通格式
<translation>One</translation>:text字段值为One,numerusForm为空列表 - 复数格式
<translation><numerusform>Thing</numerusform>...</translation>:text字段为null,numerusForm为对应复数条目列表
内容的提问来源于stack exchange,提问作者Robert Jones
相关产品推荐
相关产品推荐

