You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

多个基于XMLStreamWriter的Jackson XML自定义序列化器抛出异常

问题分析与解决方案

这个问题的核心在于你直接操作底层XMLStreamWriter时,破坏了Jackson XML生成器(ToXmlGenerator)的内部状态一致性,导致序列化流程的状态错乱。让我一步步拆解原因和解决办法:

为什么会抛出异常?

当Jackson序列化TestJacksonXml1这类Bean时,XmlBeanSerializer会按固定流程处理每个属性:

  1. 先调用ToXmlGenerator.writeFieldName()输出属性对应的XML外层元素(比如<obj1>);
  2. 再调用你的自定义序列化器处理属性值。

但你的自定义序列化器里,直接用XMLStreamWriter写了完整的独立元素(比如<class1>10</class1>),这就造成了两个关键冲突:

  • ToXmlGenerator已经写入了<obj1>的开始标签,而你的序列化器又额外插入了一套完整的元素结构,导致XML流的实际状态和ToXmlGenerator的内部记录完全不符;
  • 当第一个序列化器执行完毕,ToXmlGenerator还以为自己处于<obj1>元素内部,接下来处理obj2时会尝试写入<obj2>的字段名,此时XML流的状态已经混乱,直接触发"期望值却写了字段名"的异常。

而只保留一个序列化器时,虽然状态依然不对,但因为没有后续属性的校验,问题被暂时隐藏了——本质上这种写法也是不符合Jackson XML模块设计的。

两种可行的解决方案

方案1:调整序列化逻辑,配合ToXmlGenerator的状态

既然ToXmlGenerator已经帮你处理了属性的外层元素(比如<obj1>),你的自定义序列化器只需要负责写入该元素内部的内容即可,不要自己写完整的独立元素:

修改CustomClass1Serializer的serialize方法:

@Override
public void serialize(CustomClass1 customClass1, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
    final ToXmlGenerator toXmlGenerator = (ToXmlGenerator) jsonGenerator;
    final XMLStreamWriter staxWriter = toXmlGenerator.getStaxWriter();
    try {
        // 只写<obj1>内部的内容,不要写外层独立元素
        staxWriter.writeStartElement("class1");
        staxWriter.writeCharacters(String.valueOf(customClass1.prop1));
        staxWriter.writeEndElement();
    } catch (XMLStreamException e){
        throw new IOException(e); // 不要仅打印异常,抛出给Jackson统一处理
    }
}

同理修改CustomClass2Serializer后,ToXmlGenerator的内部状态会和XML流保持一致:先写<obj1>,序列化器在内部写入<class1>10</class1>,然后ToXmlGenerator自动补全</obj1>,后续处理obj2时状态完全正常。

方案2:使用Jackson XML专属的XmlSerializer(更推荐)

Jackson XML模块提供了XmlSerializer类,它专门适配XML序列化的状态管理,能从根源避免状态不一致问题。你可以继承它实现自定义逻辑:

以CustomClass1Serializer为例:

static class CustomClass1Serializer extends XmlSerializer<CustomClass1> {
    @Override
    public void serialize(CustomClass1 value, XmlGenerator gen, SerializerProvider provider) throws IOException {
        XMLStreamWriter staxWriter = gen.getStaxWriter();
        try {
            staxWriter.writeStartElement("class1");
            staxWriter.writeCharacters(String.valueOf(value.prop1));
            staxWriter.writeEndElement();
        } catch (XMLStreamException e) {
            throw new IOException(e);
        }
    }

    // 重写isEmpty方法,告诉Jackson对象是否为空
    @Override
    public boolean isEmpty(SerializerProvider provider, CustomClass1 value) {
        return value == null;
    }
}

同时在TestJacksonXml1的属性上通过@JacksonXmlProperty指定外层元素名,确保序列化结构符合预期:

public class TestJacksonXml1 {
    @JsonSerialize(using = CustomClass1.CustomClass1Serializer.class)
    @JacksonXmlProperty(localName = "obj1")
    CustomClass1 obj1;

    @JsonSerialize(using = CustomClass2.CustomClass2Serializer.class)
    @JacksonXmlProperty(localName = "obj2")
    CustomClass2 obj2;

    // 构造方法、getter方法保持不变
}

这种写法完全遵循Jackson XML模块的设计规范,状态管理更可靠,也更适合复杂的XML嵌套需求。

为什么用JsonGenerator就没问题?

当你使用JsonGenerator的标准方法(比如writeString、writeObject)时,它会自动同步内部状态和底层XMLStreamWriter的操作,不会出现状态脱节的情况。而直接操作XMLStreamWriter相当于绕开了Jackson的状态管理机制,自然容易引发异常。

内容的提问来源于stack exchange,提问作者mac

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 06:27:32