Spring Boot:使用自定义序列化器 + 反序列化器消费和生成 XML

Posted

技术标签:

【中文标题】Spring Boot:使用自定义序列化器 + 反序列化器消费和生成 XML【英文标题】:SpringBoot: Consume & Produce XML with a Custom Serializer + Deserializer 【发布时间】:2020-01-30 17:24:22 【问题描述】:

我有一个 SpringBoot 服务:

型号

public class Payload 
    private final String id;

    public Payload(String id)
        this.id = id;
    

    public String getId() 
        return this.id;
    

控制器

@RestController
@RequestMapping("/payload")
public class PayloadController 

    @RequestMapping(method = RequestMethod.POST)
    public Payload post(@RequestBody final Payload payload) 
        return payload;
    

我需要这个控制器能够处理 JSON 和 XML 请求并以相同的格式响应。 如果我将 Content-TypeAccept 标头设置为正确的媒体类型,这可以正常工作。

但是,我的 XML 有效负载的结构需要与我的 JSON 稍有不同:

XML:

<Payload>
    <id value="some-value"/>
</Payload>

JSON:


    id: "some-value"

我如何确保我的id 被包装在一个 xml 节点中并具有“值”作为属性?


我曾尝试在我的Payload 类上使用@JsonSerialize@JsonDeserialize 注释,但一旦我这样做,当POSTing XML 时出现以下错误


    "timestamp": "2019-10-01T12:06:35.593+0000",
    "status": 415,
    "error": "Unsupported Media Type",
    "message": "Content type 'application/xml;charset=UTF-8' not supported",
    "path": "/payload"

【问题讨论】:

【参考方案1】:

您需要注册2个转换器:

    org.springframework.http.converter.json.MappingJackson2HttpMessageConverterJSONorg.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverterXML

因为Payload 类适合JSON 有效负载,您只需添加JsonCreatorJsonProperty 注释即可使其工作:

import com.fasterxml.jackson.annotation.JsonCreator;
import com.fasterxml.jackson.annotation.JsonProperty;

public class Payload 

    private final String id;

    @JsonCreator
    public Payload(@JsonProperty(value = "id") String id) 
        this.id = id;
    

    public String getId() 
        return this.id;
    

XMLpayload 默认不适合,所以我们需要实现自定义序列化器:

import com.example.demo.model.Payload;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.dataformat.xml.ser.ToXmlGenerator;

import java.io.IOException;

public class PayloadXmlSerializer extends JsonSerializer<Payload> 

    @Override
    public void serialize(Payload value, JsonGenerator gen, SerializerProvider serializers) throws IOException 
        ToXmlGenerator toXmlGenerator = (ToXmlGenerator) gen;
        toXmlGenerator.writeStartObject();

        toXmlGenerator.writeObjectFieldStart("id");
        toXmlGenerator.setNextIsAttribute(true);
        toXmlGenerator.writeFieldName("value");
        toXmlGenerator.writeString(value.getId());
        toXmlGenerator.setNextIsAttribute(false);
        toXmlGenerator.writeEndObject();

        toXmlGenerator.writeEndObject();
    

和反序列化器:

import com.example.demo.model.Payload;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.core.JsonPointer;
import com.fasterxml.jackson.core.TreeNode;
import com.fasterxml.jackson.databind.DeserializationContext;
import com.fasterxml.jackson.databind.JsonDeserializer;
import com.fasterxml.jackson.databind.node.TextNode;

import java.io.IOException;

public class PayloadXmlDeserializer extends JsonDeserializer<Payload> 

    @Override
    public Payload deserialize(JsonParser p, DeserializationContext ctxt) throws IOException 
        TreeNode root = p.readValueAsTree();
        TreeNode value = root.at(JsonPointer.compile("/id/value"));
        if (value.isMissingNode()) 
            return new Payload(null);
        
        TextNode textNode = (TextNode)value;
        return new Payload(textNode.textValue());
    

最后,我们需要在上面注册HTTP转换器和自定义序列化器/反序列化器:

import com.example.demo.model.Payload;
import com.example.jackson.PayloadXmlDeserializer;
import com.example.jackson.PayloadXmlSerializer;
import com.fasterxml.jackson.databind.module.SimpleModule;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.converter.xml.MappingJackson2XmlHttpMessageConverter;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@EnableWebMvc
@Configuration
public class WebConfig implements WebMvcConfigurer 

    @Override
    public void configureMessageConverters(List<HttpMessageConverter<?>> converters) 
        //JSON
        converters.add(new MappingJackson2HttpMessageConverter());

        // XML
        converters.add(new MappingJackson2XmlHttpMessageConverter(Jackson2ObjectMapperBuilder
                .xml()
                .modules(payloadModule())
                .build()));
    

    public SimpleModule payloadModule() 
        SimpleModule module = new SimpleModule();
        module.addDeserializer(Payload.class, new PayloadXmlDeserializer());
        module.addSerializer(Payload.class, new PayloadXmlSerializer());

        return module;
    

另见:

Using Jackson to add XML attributes to manually-built node-tree 415 Unsupported MediaType for POST request in spring application Spring MVC

【讨论】:

以上是关于Spring Boot:使用自定义序列化器 + 反序列化器消费和生成 XML的主要内容,如果未能解决你的问题,请参考以下文章

Spring boot 动态/注解自定义 JSON 反序列化器

杰克逊自定义反序列化器在 Spring Boot 中不起作用

如何在自定义反序列化器 Spring Boot 中读取路径变量或 URL 参数

Spring Boot - 自定义 JsonDeserializer 被忽略

Spring Boot 1.4 自定义内部 Jackson 反序列化

Spring @RestController 自定义 JSON 反序列化器