JAXB 使用记录 bean转xml xml转bean 数组 继承

JAXB 使用记录

部分内容引自
https://blog.csdn.net/gengzhy/article/details/127564536

基础介绍

JAXBContext类:是应用的入口,用于管理XML/Java绑定信息
Marshaller接口:将Java对象序列化为XML数据
Unmarshaller接口:将XML数据反序列化为Java对象
@XmlRootElement:将Java类或枚举类型映射到XML元素,用在Java类上,用于标注该类是xml的一个根节点
@XmlElement:将Java类的一个属性映射到与属性同名的一个XML元素。通常与@XmlTransient搭配使用。
@XmlTransient:通常与 @XmlElement 须搭配使用的。@XmlElement用在属性上,用于指定生成xml的节点名,@XmlTransient用在对应的getter方法上,起到关联的作用
@XmlElementWrapper :对于数组或集合(即包含多个元素的成员变量),生成一个包装该数组或集合的XML元素(称为包装器)。通常配合XmlElement一起使用,XmlElementWrapper指定数组名,XmlElement指定生成xml的节点名
@XmlElementRef:用在类属性的getter方法上(即该属性是一个JavaBean),并且该属性是某些子类的父类,起到引用的作用。同时标注得有@XmlElementRef的类属性,其子类上需要使用@XmlRootElement标注,否则转换异常,提示找不到具体的引用实现。另外,转换时,需要将其子类的class一起传递到JAXBContext上下文中,否则也无法转换
@XmlAccessorOrder:控制JAXB 绑定类中属性和字段的排序
@XmlType:将Java类或枚举类型映射到XML模式类型
@XmlAccessorType(XmlAccessType.FIELD) :控制字段或属性的序列化。FIELD表示JAXB将自动绑定Java类中的每个非静态的(static)、非瞬态的(由@XmlTransient标 注)字段到XML。还有XmlAccessType.PROPERTY和XmlAccessType.NONE
@XmlJavaTypeAdapter:使用定制的适配器(即扩展抽象类XmlAdapter并覆盖marshal()和unmarshal()方法),以序列化Java类为XML
@XmlAttribute:将Java类的一个属性映射到与属性同名的一个XML属性

工具类

package xmlAndBean;import lombok.extern.slf4j.Slf4j;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import org.xml.sax.XMLReader;import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParserFactory;
import javax.xml.transform.Source;
import javax.xml.transform.sax.SAXSource;
import java.io.StringReader;
import java.io.StringWriter;/*** @version : 1.0* @Description : xml工具类* @Date: 2023/10/9 9:03**/public class XmlBeanUtils {/*** 构造方法私有**/private XmlBeanUtils() {}public static final String ENCODING_UTF = "UTF-8";public static final String ENCODING_GB = "GB18030";/*** @return java.lang.String* @Description : bean转xml不含报文头(GB18030编码格式)* @Param [obj]* @Param encoding 编码格式**/public static String beanToXml(Object obj,String encoding) {String result = null;try {JAXBContext context = JAXBContext.newInstance(obj.getClass());Marshaller marshaller = context.createMarshaller();// Marshaller.JAXB_FORMATTED_OUTPUT 决定是否在转换成xml时同时进行格式化(即按标签自动换行,否则即是一行的xml)marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);// Marshaller.JAXB_ encoding  xml的编码方式marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);// 去掉生成xml的默认报文头marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);StringWriter writer = new StringWriter();marshaller.marshal(obj, writer);result = writer.toString();} catch (Exception e) {
//            log.error("bean转xml报文失败", e);}return result;}/*** @return java.lang.String* 报文格式:* <?xml version="1.0" encoding="GB18030"?>* <ROOT>* <HEAD>* …* </HEAD>* <DATA>* <title1 >…</title1>* <title2 >…</title2>* …* </DATA>* </ROOT>* @Description : 组装报文* @Param [xmlHead, xmlData]**/public static String indentFormat(String xmlHead, String xmlData) {try {String xmlHeader = "<?xml version=\"1.0\" encoding=\"GB18030\"?>\n";StringBuilder xml = new StringBuilder();xml.append(xmlHeader).append("<ROOT>\n").append(xmlHead).append(xmlData).append("\n</ROOT>");return xml.toString();} catch (Exception e) {
//            log.error("组装xml报文失败", e);return null;}}/*** xml转对象** @param xml* @param msgVo* @param <T>* @return*/public static <T> T xmlToBean(String xml, Class<T> msgVo) {if (msgVo == null) {return null;}try {JAXBContext context = JAXBContext.newInstance(msgVo);Unmarshaller unmarshaller = context.createUnmarshaller();Source source = trunSource(xml);return (T) unmarshaller.unmarshal(source);} catch (Exception e) {
//            log.error("xml转对象异常:", e);}return null;}/*** 忽略xml命名空间** @param xmlStr* @return* @throws SAXException* @throws ParserConfigurationException*/private static Source trunSource(String xmlStr) throws SAXException, ParserConfigurationException {StringReader reader = new StringReader(xmlStr);SAXParserFactory sax = SAXParserFactory.newInstance();sax.setNamespaceAware(false);XMLReader xmlReader = sax.newSAXParser().getXMLReader();return new SAXSource(xmlReader, new InputSource(reader));}/*** Java Bean 转 Xml** @param bean         - Java Bean* @param inheritClazz - Java Bean中嵌套的类,且有继承关系的Java Class* @return - xml*/public static String beanToXml(Object bean, String encoding,Class<?>... inheritClazz) {try {JAXBContext context = initContext(bean.getClass(), inheritClazz);Marshaller marshaller = context.createMarshaller();// 格式化xmlmarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);// Marshaller.JAXB_ encoding  xml的编码方式marshaller.setProperty(Marshaller.JAXB_ENCODING, encoding);// 去掉生成xml的默认报文头marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);StringWriter writer = new StringWriter();marshaller.marshal(bean, writer);return writer.toString();} catch (JAXBException e) {e.printStackTrace();}return null;}/*** Xml 转 Java Bean** @param xml          - xml* @param beanClazz    - Java Bean Class* @param inheritClazz - Java Bean中嵌套的类,且有继承关系的Java Class* @return - bean*/public static Object xmlToBean(String xml, Class<?> beanClazz, Class<?>... inheritClazz) {try {JAXBContext context = initContext(beanClazz, inheritClazz);Unmarshaller um = context.createUnmarshaller();StringReader sr = new StringReader(xml);return um.unmarshal(sr);} catch (JAXBException e) {e.printStackTrace();}return null;}/*** 初始化JAXBContext** @param mainClazz    - 序列化或反序列化Class* @param inheritClazz - Java Bean中嵌套的类,且有继承关系的Java Class* @return - JAXBContext*/private static JAXBContext initContext(Class<?> mainClazz, Class<?>... inheritClazz) throws JAXBException {JAXBContext context;if (inheritClazz != null) {Class<?>[] clazzArr = new Class[inheritClazz.length + 1];clazzArr[0] = mainClazz;System.arraycopy(inheritClazz, 0, clazzArr, 1, clazzArr.length - 1);context = JAXBContext.newInstance(clazzArr);} else {context = JAXBContext.newInstance(mainClazz);}return context;}}

xml结构

结构如下:

<ROOT>
<HEAD></HEAD>
<DATA><title1 ></title1><title2 ></title2><RECORD><LIST1 p_type="G"><title1 ></title1></LIST1><LIST1 p_type="G"><title1 ></title1></LIST1></RECORD>
</DATA>
</ROOT>

①存在继承关系的bean转xml

基类:用于统一,方便向下转型

/*** @version : 1.0* @Description : 基类* @Date: 2023/10/9 19:55**/
public class BaseDTO {
}

DATA标签实体类同时继承基类

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.util.List;/*** @version : 1.0* @Description :* @Date: 2023/10/9 19:55**/
@Setter
@Getter
@ToString
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "DATA")
public class BookDTO extends BaseDTO{@XmlElement(name = "name")@XmlJavaTypeAdapter(CdataXmlAdapter.class)private String bookName;@XmlElement(name = "id")private String bookId;//@XmlElementWrapper(name = "RECORD")
//    @XmlElement(name = "LIST1")
//    private List<DescDTO> descDTOList;
}

HEAD标签实体类

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;/*** @version : 1.0* @Description :* @Date: 2023/10/9 19:58**/
@Getter
@Setter
@ToString
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "HEAD")
public class HeadDTO {@XmlElement(name = "SEND_NODE")private String send;@XmlElement(name = "RECV_NODE")private String receive;
}

根标签实体类
其中针对DATA标签,每个接口的DATA标签可能都不相同,这里使用了@XmlElementRef标签结合基类(BaseDTO ),在使用中将接口的实体类继承BaseDTO基类,并标注@XmlRootElement(name = “DATA”),即可实现每个接口拥有独立的对象实现bean转xml

import lombok.*;import javax.xml.bind.annotation.*;/*** @version : 1.0* @Description :* @Date: 2023/10/9 19:55**/
@Setter
@Getter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@XmlAccessorType(XmlAccessType.FIELD)
//@XmlType(propOrder = { "head", "data" }) 这句加不加无所谓
@XmlRootElement(name = "ROOT")
public class RootDTO {@XmlElement(name = "HEAD")private HeadDTO head;//@XmlElement(name = "DATA")//@XmlElementRef 注解要加载对象上不能加在get方法上,不然报错如下//com.sun.xml.internal.bind.v2.runtime.IllegalAnnotationsException: 5 counts of IllegalAnnotationExceptions类的两个属性具有相同名称 "head"@XmlElementRef(name = "DATA")private BaseDTO data;}

测试类

public class XmlTest {public static void main(String[] args) {//继承基类BookDTO bookDTO = new BookDTO();bookDTO.setBookName("name");bookDTO.setBookId("id");//HEAD标签实体类,一般内容固定HeadDTO headDTO = new HeadDTO();headDTO.setSend("send");headDTO.setReceive("rece");RootDTO rootDTO = new RootDTO();rootDTO.setHead(headDTO);//因为BookDTO 继承基类所以此处能设置成功rootDTO.setData(bookDTO);//bean转xml,需要将BookDTO.class(子类)传入以告知System.out.println(XmlBeanUtils.beanToXml(rootDTO,XmlBeanUtils.ENCODING_GB,BookDTO.class));//xml转beanSystem.out.println(XmlBeanUtils.xmlToBean(xml,  RootDTO.class,BookDTO.class));}
}

运行结果:
在这里插入图片描述

②存在数组的bean转xml

DATA标签实体类同时继承基类

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;import javax.xml.bind.annotation.*;
import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter;
import java.util.List;/*** @version : 1.0* @Description :* @Date: 2023/10/9 19:55**/
@Setter
@Getter
@ToString
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "DATA")
public class BookDTO extends BaseDTO{@XmlElement(name = "name")@XmlJavaTypeAdapter(CdataXmlAdapter.class)private String bookName;@XmlElement(name = "id")private String bookId;//@XmlElementWrapper(name = "RECORD")@XmlElement(name = "LIST1")private List<DescDTO> descDTOList;
}

数组实体类对象

import lombok.Getter;
import lombok.Setter;
import lombok.ToString;import javax.xml.bind.annotation.*;/*** @version : 1.0* @Description :* @Date: 2023/10/10 10:25**/
@Getter
@Setter
@ToString
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "LIST1")
public class DescDTO{@XmlAttribute(name = "p_type")private String pType;@XmlElement(name = "description")private String description;
}

假设在DATA标签实体类中存在数组属性,则使用@XmlElement(name = “LIST1”)进行标注,即可实现每个数组对象转换成xml时都被<LIST1> </LIST1>包裹
测试类

public class XmlTest {public static void main(String[] args) {BookDTO bookDTO = new BookDTO();bookDTO.setBookName("name");bookDTO.setBookId("id");HeadDTO headDTO = new HeadDTO();headDTO.setSend("send");headDTO.setReceive("rece");RootDTO rootDTO = new RootDTO();rootDTO.setHead(headDTO);rootDTO.setData(bookDTO);DescDTO descDTO = new DescDTO();//descDTO.setPType("G");descDTO.setDescription("desc11111");DescDTO descDTO1 = new DescDTO();//descDTO1.setPType("G");descDTO1.setDescription("desc22222");ArrayList<DescDTO> descDTOS = new ArrayList<>();descDTOS.add(descDTO);descDTOS.add(descDTO1);bookDTO.setDescDTOList(descDTOS);String xml = XmlBeanUtils.beanToXml(rootDTO, XmlBeanUtils.ENCODING_GB, BookDTO.class);//bean转xmlSystem.out.println(XmlBeanUtils.beanToXml(rootDTO,XmlBeanUtils.ENCODING_GB,BookDTO.class));//xml转beanSystem.out.println(XmlBeanUtils.xmlToBean(xml,  RootDTO.class,BookDTO.class));}
}

运行结果
在这里插入图片描述
如果需要再在数组外面套一层标签则:

    @XmlElementWrapper(name = "RECORD")@XmlElement(name = "LIST1")private List<DescDTO> descDTOList;

运行结果:
在这里插入图片描述

③存在数组且数组前后标签要求不一致的bean转xml

<LIST1 p_type="G">标签开始,以</LIST1>标签结束
在数组对象中引入@XmlAttribute

    @XmlAttribute(name = "p_type")private String pType;

测试类:

//设置属性
descDTO.setPType("G");
public class XmlTest {public static void main(String[] args) {BookDTO bookDTO = new BookDTO();bookDTO.setBookName("name");bookDTO.setBookId("id");HeadDTO headDTO = new HeadDTO();headDTO.setSend("send");headDTO.setReceive("rece");RootDTO rootDTO = new RootDTO();rootDTO.setHead(headDTO);rootDTO.setData(bookDTO);DescDTO descDTO = new DescDTO();descDTO.setPType("G");descDTO.setDescription("desc11111");DescDTO descDTO1 = new DescDTO();descDTO1.setPType("G");descDTO1.setDescription("desc22222");ArrayList<DescDTO> descDTOS = new ArrayList<>();descDTOS.add(descDTO);descDTOS.add(descDTO1);bookDTO.setDescDTOList(descDTOS);String xml = XmlBeanUtils.beanToXml(rootDTO, XmlBeanUtils.ENCODING_GB, BookDTO.class);//bean转xmlSystem.out.println(XmlBeanUtils.beanToXml(rootDTO,XmlBeanUtils.ENCODING_GB,BookDTO.class));//xml转beanSystem.out.println(XmlBeanUtils.xmlToBean(xml,  RootDTO.class,BookDTO.class));}
}

运行结果:
在这里插入图片描述

④针对<![CDATA[文本内容]]>标签

import javax.xml.bind.annotation.adapters.XmlAdapter;/*** @version : 1.0* @Description : <![CDATA]>* @Date: 2023/10/8 19:03**/
public class CdataXmlAdapter extends XmlAdapter<String, String> {public CdataXmlAdapter() {}public String marshal(String arg0) throws Exception {return "<![CDATA[" + arg0 + "]]>";}public String unmarshal(String arg0) throws Exception {return arg0;}
}

使用@XmlJavaTypeAdapter注解,在需要的字段上

	@XmlElement(name = "name")@XmlJavaTypeAdapter(CdataXmlAdapter.class)private String bookName;

运行结果:
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/101325.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spark入门

Spark 1.Spark概述 2.Spark特点 3.RDD概述 1. Spark概述 什么是Spark 回顾&#xff1a;Hadoop主要解决&#xff0c;海量数据的存储和海量数据的分析计算。 Spark是一种基于内存的快速、通用、可扩展的大数据分析计算引擎。 Hadoop与Spark历史 Hadoop与Spark框架对比 Dr…

【gmail注册教程】手把手教你注册Google邮箱账号

手把手教你注册Google邮箱账号 写在前面&#xff1a; 要注意&#xff0c;注册Google邮箱必须要确保自己能够 科学上网&#xff0c;如果暂时做不到&#xff0c;请先进行相关学习。使用的手机号是大陆&#xff08;86&#xff09;的。 在保证自己能够科学上网后&#xff0c;在浏…

如何在 Spring Boot 中使用 WebSocket

在Spring Boot中使用WebSocket构建实时应用 WebSocket是一种用于实现双向通信的网络协议&#xff0c;它非常适合构建实时应用程序&#xff0c;如在线聊天、实时通知和多人协作工具。Spring Boot提供了对WebSocket的支持&#xff0c;使得在应用程序中集成WebSocket变得非常容易…

3D包容盒子

原理简述 包围体&#xff08;包容盒&#xff09;是一个简单的几何空间&#xff0c;里面包含着复杂形状的物体。为物体添加包围体的目的是快速的进行碰撞检测或者进行精确的碰撞检测之前进行过滤&#xff08;即当包围体碰撞&#xff0c;才进行精确碰撞检测和处理&#xff09;。包…

Rule-Engine-Starter V1.0.0

一个轻量级的规则引擎、搜索引擎&#xff0c;让条件匹配简单、优雅。 GIT地址 https://gitcode.cosmoplat.com/15011240224/rule-engine-starter 介绍 Rule-Engine-Starter 是一个轻量级规则引擎&#xff0c;V1.0.0主要解决条件匹配问题。比如飞书文档&#xff0c;每个文档都…

【网络安全 ---- 靶场搭建】凡诺企业网站管理系统靶场详细搭建过程(asp网站,练习Access数据库的 sql注入)

一&#xff0c;资源下载 百度网盘资源下载链接&#xff1a;百度网盘 请输入提取码百度网盘为您提供文件的网络备份、同步和分享服务。空间大、速度快、安全稳固&#xff0c;支持教育网加速&#xff0c;支持手机端。注册使用百度网盘即可享受免费存储空间https://pan.baidu.com…

【Mysql实现递归树查询】

Mysql8实现递归查询 递归执行分析demo数据查询demo数据扩展字段扩展 大家好! 在我们日常工作中&#xff0c;经常会遇到一些问题,它们的一些解决方案通常会用到递归这一强大的技术手段。递归不仅能帮助我们更高效的解决问题,还可以使代码更简介、更易于理解, 今天我来给大家分享…

竹云筑基,量子加密| 竹云携手国盾量子构建量子身份安全防护体系

9月23日-24日&#xff0c;2023量子产业大会在安徽合肥举行。作为量子科技领域行业盛会&#xff0c;2023年量子产业大会以“协同创新 量点未来”为主题&#xff0c;展示了前沿的量子信息技术、产业创新成果&#xff0c;并举办主旨论坛、量子科普讲座等系列专项活动。量子信息作为…

云服务器带宽对上传下载速度的影响

简单来说就是 云服务器收到数据代表入&#xff0c;带宽大小 < 10时&#xff0c;入带宽大小10 带宽大小 > 10时&#xff0c;出入带宽上限 等于实际购买时候的大小

金融信创黄金三年:小程序生态+跨端技术框架构建

小程序应用场景生态的发展&#xff0c;受益于开源技术的发展&#xff0c;以及响应快速开发的实际业务需求&#xff0c;一些跨端框架如&#xff1a;Electron、wxPython、FinClip、Tauri、Flutter等发展也非常迅速&#xff0c;小程序生态跨端技术框架&#xff0c;不仅能满足自有超…

生活中的视音频技术

生活中的视音频技术 平时我们打开电脑中自己存电影的目录的话&#xff0c;一般都会如下图所示&#xff0c;一大堆五花八门的电影。&#xff08;其实专业的影视爱好者一概会把影视文件分门别类的&#xff0c;但我比较懒&#xff0c;一股脑把电影放在了一起&#xff09; 因为下载…

10.9~10.10

触发器方程 触发器之间的转化 触发器需要输入和输出 对于D触发器&#xff0c;只需要D信号的输入 对于jk触发器&#xff0c;至少需要原状态的与非信号&#xff0c;并且需要j,k信号 假如确定y2,则D触发器需要什么D信号&#xff0c;是需要卡诺图确定 j-k触发器需要什么jk信号…

经典循环命题:百钱百鸡

翁五钱一只&#xff0c;母三钱&#xff0c;小鸡三只一钱&#xff1b;百钱百鸡百鸡花百钱。 (本笔记适合能熟练应用for循环、会使if条件分支语句、能格式化字符输出的 coder 翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a…

【数据结构】二叉树的链式结构及实现

目录 1. 前置说明 2. 二叉树的遍历 2.1 前序、中序以及后序遍历 2.2 层序遍历 3. 节点个数及高度等 4. 二叉树的创建和销毁 1. 前置说明 在学习二叉树的基本操作前&#xff0c;需先要创建一棵二叉树&#xff0c;然后才能学习其相关的基本操作。由于现在大家对二叉树结构…

区块链跨链技术

区块链跨链技术 背景 近年来&#xff0c;随着区块链技术的不断发展&#xff0c;区块链的应用场景逐渐从最初的加密货币领域扩展到金融、物流、医疗、公共服务等各个领域。随着区块链的应用场景不断增多&#xff0c;区块链的“数据孤岛”问题日益突出&#xff0c;不同场景下的…

生信学院|10月13日《SOLIDWORKS参数化应用——DriveWorksXpress》

课程主题&#xff1a;SOLIDWORKS参数化应用——DriveWorksXpress 课程时间&#xff1a;2023年10月13日 14:00-14:30 主讲人&#xff1a;温晓露 生信科技 售后服务工程师 1、DriveWorks的作用 2、用 DriveWorksXpress 自动化您的设计过程 3、Drive Works Xpress最佳做法 4…

如何将jpg转化为png?

如何将jpg转化为png&#xff1f;可能有的小伙伴就会疑惑了&#xff0c;jpg和png都是图片常用的一种格式&#xff0c;为什么要进行格式的更改呢&#xff1f;那是因为PNG格式具有更好的图片质量和更少的失真。JPG&#xff08;或JPEG&#xff09;格式的图片通常是压缩过的&#xf…

Mall脚手架总结(二) —— SpringData操作Elasticsearch

前言 万字长文带你弄清楚SpringData中的Elasticsearch操作以及在脚手架里接口的结构关系&#xff01;经过前面鉴证授权的整合&#xff0c;荔枝开始熟悉项目的学习的方法了&#xff0c;虽然脚手架中的内容比较简单&#xff0c;但是把边角的知识点全部扫到还是比较花时间的尤其是…

C#和JS交互之Microsoft.ClearScript.V8(V8引擎)

之前测试了很多JS引擎&#xff0c;都只支持es5语法&#xff0c;不支持执行es6&#xff0c;测试了下微软的V8反正能跑通&#xff0c;应该是支持的。还得是微软呀。 如图&#xff1a;安装相关包&#xff1a; 这是参考的官方V8代码 using Microsoft.ClearScript.JavaScript; us…

当下测试行业中UI自动化面临的难点及如何解决

经常有人会问&#xff0c;什么样的项目才适合进行UI自动化测试呢&#xff1f;UI自动化测试相当于模拟手工测试&#xff0c;通过程序去操作页面上的控件。而在实际测试过程中&#xff0c;经常会遇到无法找到控件&#xff0c;或者因控件定义变更而带来的维护成本等问题。 哪些场…