XML反序列化

1、基本介绍

在工作中,经常为了调通上游接口,从而对请求第三方的参数进行XML序列化,这里常使用的方式就是使用JAVA扩展包中的相关注解和类来实现xml的序列化和反序列化。

2、自定义工具类

java 复制代码
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 1、实现 对象 转 xml
 * 2、实现 xml 转对象
 */
public class XmlInterfaceUtils {
    private static final ConcurrentHashMap<Class<?>, JAXBContext> contextMap =
            new ConcurrentHashMap<>();


    private static JAXBContext context(Class<?> clazz) {
        // JAXBContext 是线程安全的,可以在多个线程中复用
        // computeIfAbsent 方法,如果map集合存在相同的key,则覆盖value值;不存在相同key,则添加到map集合中
        return contextMap.computeIfAbsent(clazz, cls -> {
            try {
                return JAXBContext.newInstance(cls);
            } catch (JAXBException e) {
                throw new IllegalStateException(e);
            }
        });
    }

    public static String convertToXml(Object obj) {
        StringWriter sw = new StringWriter();
        JAXBContext context = context(obj.getClass());
        Marshaller marshaller;
        try {
            marshaller = context.createMarshaller();

            //1.格式化输出,即按标签自动换行,否则就是一行输出
            marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT,
                    Boolean.TRUE);

            //2.设置编码(默认编码就是utf-8)
//            marshaller.setProperty(Marshaller.JAXB_ENCODING, "UTF-8");

            //3.是否省略xml头信息,默认不省略(false)
            //   <?xml version="1.0" encoding="UTF-8">  这一句就是"头信息"
//            marshaller.setProperty(Marshaller.JAXB_FRAGMENT, false);

            marshaller.marshal(obj, sw);
        } catch (JAXBException e) {
            throw new IllegalStateException(e);
        }
        return sw.toString();
    }



    /**
     * xml转object
     *
     * @param clazz 转换类
     * @param xml   XML 字符串
     * @param <T>   对象类型
     * @return 转换结果
     */
    public static <T> T xmlToObject(Class<T> clazz, String xml) {
        JAXBContext context = context(clazz);

        // 每次都创建 Unmarshaller
        Unmarshaller unmarshaller;
        try {
            unmarshaller = context.createUnmarshaller();
        } catch (JAXBException e) {
            throw new IllegalStateException(e);
        }

        StringReader reader = new StringReader(xml);
        T message;
        try {
            message = (T) unmarshaller.unmarshal(reader);
        } catch (JAXBException e) {
            throw new IllegalStateException(e);
        }
        return message;
    }
}

3、模拟请求第三方的请求参数-V1.0

3.1 定义业务实体

Provider类

java 复制代码
import javax.xml.bind.annotation.*;


@XmlRootElement
public class Provider {
    
    private User user;

    private String id;

    private Integer providerTelephone;

    private String providerAddress;


    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Integer getProviderTelephone() {
        return providerTelephone;
    }

    public void setProviderTelephone(Integer providerTelephone) {
        this.providerTelephone = providerTelephone;
    }

    public String getProviderAddress() {
        return providerAddress;
    }

    public void setProviderAddress(String providerAddress) {
        this.providerAddress = providerAddress;
    }
}

User类

java 复制代码
public class User {
    private String username;
    private String password;

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

3.2 运行代码

java 复制代码
public class Application {

    public static void main(String[] args) {
        Provider provider = new Provider();
        User user = new User();
        user.setUsername("hu");
        user.setPassword("123456");
        provider.setUser(user);
        provider.setProviderTelephone(4008123);
        provider.setProviderAddress("BeiJing");
        provider.setId("No.1");

        //序列化成xml格式的字符串
        String xml = XmlInterfaceUtils.convertToXml(provider);
        System.out.println(xml);

        //反序列化成对象
        Provider provider1 = XmlInterfaceUtils.xmlToObject(Provider.class, xml);

    }
}

控制台打印结果

必须要有一个@XmlRootElement用来标记哪个类作为根节点。否则,反序列化会失败,提示缺少 @XmlRootElement注解。

4、模拟请求第三方的请求参数-V2.0

假如第三方发生改变,要求我们进行适配。

将Provider类原本的id标签设置为根节点的属性,其他标签全部首字母大写,且按照手机号码,地址,用户信息的顺序进行反序列化,而User类的标签仍然是小写开头。

java 复制代码
mport javax.xml.bind.annotation.*;

@XmlType(
        //指定序列化的时候,生成每个标签的顺序,不指定的话,默认按照从上到下的顺序生成
        propOrder = {"providerTelephone", "providerAddress", "user","id"}
)
@XmlRootElement(name = "Provider")
@XmlAccessorType(XmlAccessType.FIELD)
public class Provider {
    
    @XmlElement(name = "User")
    private User user;

    //该字段映射为一个属性
    @XmlAttribute(name = "id")
    private String id;

    @XmlElement(name = "ProviderTelephone")
    private Integer providerTelephone;

    @XmlElement(name = "ProviderAddress")
    private String providerAddress;


    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public User getUser() {
        return user;
    }

    public void setUser(User user) {
        this.user = user;
    }

    public Integer getProviderTelephone() {
        return providerTelephone;
    }

    public void setProviderTelephone(Integer providerTelephone) {
        this.providerTelephone = providerTelephone;
    }

    public String getProviderAddress() {
        return providerAddress;
    }

    public void setProviderAddress(String providerAddress) {
        this.providerAddress = providerAddress;
    }
}

运行结果如下

5、@XmlAccessorType的作用

通过上面的例子可以发现,@XmlElement注解用来是生成子节点,@XmlAttribute注解用来生成节点的属性。

那@XmlAccessorType注解的作用呢?

默认序列化的时候,会根据类的get()方法生成一个子节点或者是属性,但是,我在字段名上又用@XmlElement标记了,这也会生出一个子节点。两个相同的子节点名称,就会导致反序列化失败。

因此,就需要用【 @XmlAccessorType(XmlAccessType.FIELD) 】来直接对类的字段进行映射,不考虑get方法,这样就会正常序列化。

相关推荐
爱读源码的大都督15 分钟前
为什么有了HTTP,还需要gPRC?
java·后端·架构
qq_49244844623 分钟前
Jmeter设置负载阶梯式压测场景(详解教程)
开发语言·python·jmeter
Lucky_Turtle33 分钟前
【Java Xml】Apache Commons Digester3解析
xml·java·apache
聪明的笨猪猪1 小时前
Java Redis “缓存设计”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
FIavor.1 小时前
我发送给Apifox是http://localhost:9002/goods/getByUserName?name=张三 为什么会是500哪里错了?
java·服务器·网络协议·http
ID_180079054731 小时前
京东获取整站实时商品详情数据|商品标题|数据分析提取教程
java·开发语言
微露清风2 小时前
系统性学习C++-第五讲-内存管理
java·c++·学习
计算机毕业设计木哥2 小时前
计算机毕业设计选题推荐:基于SpringBoot和Vue的快递物流仓库管理系统【源码+文档+调试】
java·vue.js·spring boot·后端·课程设计
qiuiuiu4132 小时前
正点原子RK3568学习日志-编译第一个驱动程序helloworld
linux·c语言·开发语言·单片机
235162 小时前
【LeetCode】146. LRU 缓存
java·后端·算法·leetcode·链表·缓存·职场和发展