处理动态元素或属性
动态元素
假设现在有一项解析XML的需求,该XML的大体结构是固定的,如下这样:
<response>
<errorCode></errorCode>
<errorMessage></errorMessage>
<data>
</data>
</response>
但是data节点下包含的子节点的内容是不固定的,比如它可能是这样:
<response>
<errorCode></errorCode>
<errorMessage></errorMessage>
<data>
<result1>
<result11></result11>
<result12></result12>
</result1>
</data>
</response>
也可能是下面这样:
<response>
<errorCode></errorCode>
<errorMessage></errorMessage>
<data>
<result2>
<result21></result21>
<result22></result22>
</result2>
</data>
</response>
还可能是下面这样:
<response>
<errorCode></errorCode>
<errorMessage></errorMessage>
<data>
<d1>
<d11></d11>
<d12></d12>
</d1>
<d2>
<d21></d21>
<d22></d22>
</d2>
</data>
</response>
实际情况可能比这个更复杂,笔者这里就简单的考虑上面几种情况,但是它不会影响我们对动态节点的解释。上面的XML的整体结构是固定的,但是data下面的节点内容是动态的,可能也是不可预料的。基于上面这样的需求你可能想我们把result1、result2、d1和d2都定义在data节点对应的Java类下面即可,然后在不同的XML时对应的result1或者result2或者d1、d2属性有值。这个时候对应的Java类定义可能是下面这样:
@XmlRootElement
public class Response {
private String errorCode;
private String errorMessage;
private ResultData data;
//...省略get和set方法
}
public class ResultData {
private Result1 result1;
private Result2 result2;
private D1 d1;
private D2 d2;
//...省略get和set方法
}
public class Result1 {
private String result11;
private String result12;
//...省略get和set方法
}
public class Result2 {
private String result21;
private String result22;
//...省略get和set方法
}
public class D1 {
private String d11;
private String d12;
//...省略get和set方法
}
public class D2 {
private String d21;
private String d22;
//...省略get和set方法
}
基于上面的XML这样的配置是可以的,但倘若data下面还有很多种可能性,可能有几百种甚至更多不同的结构,把它们都分别定义在ResultData里面也不太现实。这个时候就可以使用@XmlAnyElement来映射data节点下的内容了,@XmlAnyElement可以用来匹配任何不能够被精确匹配的元素,如果动态元素只会有一个则可以把需要动态映射的属性定义为Object或org.w3c.dom.Element
类型。如果有多个则需要把它定义为Object或org.w3c.dom.Element
对应的集合类型。所以对于上述需求我们可以把ResultData类定义如下:
public static class ResultData {
private List<Object> datas;
@XmlAnyElement
public List<Object> getDatas() {
return datas;
}
public void setDatas(List<Object> datas) {
this.datas = datas;
}
}
然后我们对下面这段XML转换为Java对象做一个测试:
<response>
<errorCode>0</errorCode>
<errorMessage>成功</errorMessage>
<data>
<d1>
<d11>A</d11>
<d12>B</d12>
</d1>
<d2 status="1">
<d21>D</d21>
<d22>E</d22>
</d2>
</data>
</response>
测试代码如下:
@Test
public void testDynamic() throws Exception {
JAXBContext jaxbContext = JAXBContext.newInstance(Response.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
InputStream xml = this.getClass().getClassLoader().getResourceAsStream("jaxb/dynamic.xml");
Response response = (Response) unmarshaller.unmarshal(xml);
List<Object> datas = response.getData().getDatas();
Assert.assertEquals(datas.size(), 2);
Assert.assertTrue(datas.get(0) instanceof Element);
Assert.assertTrue(datas.get(1) instanceof Element);
}
上面的测试代码是可以完全通过的,所以你没有看错,转换为对象后ResultData.getDatas()中包含两个元素,且这两个元素都是org.w3c.dom.Element
类型,因为这个时候对于JAXB来说是不识别d1和d2对应的对象的,所以只能把它们转换为org.w3c.dom.Element
类型,其实这在某些比较复杂的情况下,当某一动态节点确实不好映射为一些具体的对象时,退一步处理Element也是一种很好的方式。如果需要能转换为对应的对象,我们需要指定XmlAnyElement的lax属性为true。
public class ResultData {
private List<Object> datas;
@XmlAnyElement(lax=true)
public List<Object> getDatas() {
return datas;
}
public void setDatas(List<Object> datas) {
this.datas = datas;
}
}
D1和D2的类上需要加上@XmlRootElement,让节点与希望被转换的Java类相匹配,如果节点名称与Java类的名称不一致,可以通过@XmlRootElement的name属性指定需要匹配的节点的名称。
@XmlRootElement
public class D1 {
private String d11;
private String d12;
public String getD11() {
return d11;
}
public void setD11(String d11) {
this.d11 = d11;
}
public String getD12() {
return d12;
}
public void setD12(String d12) {
this.d12 = d12;
}
}
@XmlRootElement
public class D2 {
private String d21;
private String d22;
public String getD21() {
return d21;
}
public void setD21(String d21) {
this.d21 = d21;
}
public String getD22() {
return d22;
}
public void setD22(String d22) {
this.d22 = d22;
}
}
与此同时,在创建JAXBContext时需要加入对应的Class,改造后的单元测试代码如下,测试也是可以通过的。这说明d1节点确实被转换为D1类型的对象了,d2节点也被转换为D2类型的对象了。
@Test
public void testDynamic() throws Exception {
JAXBContext jaxbContext = JAXBContext.newInstance(Response.class, D1.class, D2.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
InputStream xml = this.getClass().getClassLoader().getResourceAsStream("jaxb/dynamic.xml");
Response response = (Response) unmarshaller.unmarshal(xml);
List<Object> datas = response.getData().getDatas();
Assert.assertEquals(datas.size(), 2);
Assert.assertTrue(datas.get(0) instanceof D1);
Assert.assertTrue(datas.get(1) instanceof D2);
}
基于这样的配置,把对象转换为XML也是可以的,这里简单的拿D1和D2示例如下。
@Test
public void testDynamic() throws Exception {
Response response = new Response();
response.setErrorCode("0");
response.setErrorMessage("成功");
ResultData data = new ResultData();
List<Object> datas = new ArrayList<>();
D1 d1 = new D1();
d1.setD11("A");
d1.setD12("B");
datas.add(d1);
D2 d2 = new D2();
d2.setD21("D");
d2.setD22("E");
datas.add(d2);
data.setDatas(datas);
response.setData(data);
JAXBContext jaxbContext = JAXBContext.newInstance(Response.class, D1.class, D2.class);
Marshaller marshaller = jaxbContext.createMarshaller();
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
marshaller.marshal(response, System.out);
}
生成的XML如下,其中的节点顺序是因为我们没有指定所以data跑前面去了,但是这不影响:
<response>
<data>
<d1>
<d11>A</d11>
<d12>B</d12>
</d1>
<d2>
<d21>D</d21>
<d22>E</d22>
</d2>
</data>
<errorCode>0</errorCode>
<errorMessage>成功</errorMessage>
</response>
有兴趣的读者可以试验开始介绍的Result1、Result2,或者是更复杂的,更动态的场景。
动态属性
考虑这样一种场景,你有一个元素上面会有很多不同的属性,这些属性是动态的,你不能完全预知到其中会包含哪些属性,或者属性太多了你不能一一把它们列举出来与对象的属性一一对应。比较典型的示例就是HTML元素,它除了标准的属性之外,有时为了程序的需要我们可能会指定一些自定义的属性,这些属性还不固定。这样的情景我们就可以通过一个Map来接收这些动态的属性,然后在上面使用@XmlAnyAttribute,这样在转换为对象时JAXB就会把那些不能精确匹配到对应的属性上的XML元素属性映射到对应的Map上。假设有类似下面这样一段XML需要应用动态的属性匹配。
<holder prop1="value1" prop2="value2" prop3="value3"/>
可以建立类似下面这样的代码进行匹配,需要注意的是Map对应的Key需要是javax.xml.namespace.QName
类型的。
@XmlRootElement(name="holder")
public class AnyAttributeHolder {
private Map<QName, String> attrs;
@XmlAnyAttribute
public Map<QName, String> getAttrs() {
return attrs;
}
public void setAttrs(Map<QName, String> attrs) {
this.attrs = attrs;
}
}
可以进行验证如下:
@Test
public void test2() {
String xml = "<holder prop1=\"value1\" prop2=\"value2\" prop3=\"value3\"/>";
AnyAttributeHolder holder = JAXB.unmarshal(new StringReader(xml), AnyAttributeHolder.class);
Assert.assertTrue(holder.getAttrs().get(new QName("prop1")).equals("value1"));
Assert.assertTrue(holder.getAttrs().get(new QName("prop2")).equals("value2"));
Assert.assertTrue(holder.getAttrs().get(new QName("prop3")).equals("value3"));
}
如果你还有把它marshal为XML的需要,也是可以进行的,如:
@Test
public void test() {
Map<QName, String> attrs = new HashMap<>();
for (int i=0; i<5; i++) {
attrs.put(new QName("prop" + (i+1)), "value" + (i+1));
}
AnyAttributeHolder holder = new AnyAttributeHolder();
holder.setAttrs(attrs);
StringWriter writer = new StringWriter();
JAXB.marshal(holder, writer);
System.out.println(writer);
}
生成的XML是如下这样:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<holder prop2="value2" prop1="value1" prop5="value5" prop4="value4" prop3="value3"/>
这就是JAXB对于动态属性的处理方式。
相关推荐
我的第一个C#小程序之简单音乐播放器1731655933.html
练习springboot1 项目 模拟高并发秒杀,实现基本的登录、查看商品列表、秒杀、下单等功能,简单实现了系统缓存、降级和限流。SpringBoot + MyBatis + MySQL+Druid + Redis + RabbitMQ + Bootstrap + jQue….zip
html常规学习.zip资源资料用户手册
【项目资源】:包含前端、后端、移动开发、操作系统、人工智能、物联网、信息化管理、数据库、硬件开发、大数据、课程资源、音视频、网站开发等各种技术项目的源码。包括STM32、ESP8266、PHP、QT、Linux、iOS、C++、Java、python、web、C#、EDA、proteus、RTOS等项目的源码。【项目质量】:所有源码都经过严格测试,可以直接运行。功能在确认正常工作后才上传。【适用人群】:适用于希望学习不同技术领域的小白或进阶学习者。可作为毕设项目、课程设计、大作业、工程实训或初期项目立项。【附加价值】:项目具有较高的学习借鉴价值,也可直接拿来修改复刻。对于有一定基础或热衷于研究的人来说,可以在这些基础代码上进行修改和扩展,实现其他功能。【沟通交流】:有任何使用上的问题,欢迎随时与博主沟通,博主会及时解答。鼓励下载和使用,并欢迎大家互相学习,共同进步。
HTML转PDF py脚本
yolo系列算法目标检测数据集,包含标签,可以直接训练模型和验证测试,数据集已经划分好,包含数据集配置文件data.yaml,适用yolov5,yolov8,yolov9,yolov7,yolov10,yolo11算法; 包含两种标签格:yolo格式(txt文件)和voc格式(xml文件),分别保存在两个文件夹中; yolo格式:<class> <x_center> <y_center> <width> <height>, 其中: <class> 是目标的类别索引(从0开始)。 <x_center> 和 <y_center> 是目标框中心点的x和y坐标,这些坐标是相对于图像宽度和高度的比例值,范围在0到1之间。 <width> 和 <height> 是目标框的宽度和高度,也是相对于图像宽度和高度的比例值
西电通院模电大作业课后题电路设计图24年
本文档主要讲述的是sqlserver内存释放;希望本文档会给有需要的朋友带来帮助;感兴趣的朋友可以过来看看
zw
发动机制造厂技术处安全、消防安全手册.docx
生产现场工艺文件执行检查管理流程说明.docx
Spring Boot集成Spring Security,HTTP请求授权配置:包含匿名访问、允许访问、禁止访问配置
通过设置截止频率和带宽来获取对应的滤波器参数
全国月尺度平均风速数据集(1961-2022, 0.25° × 0.25°)是一个高分辨率的网格化平均风速数据集,覆盖了中国大陆及周边地区。 该数据集通过科学方法整合气象观测和再分析数据,为气候研究、生态模型、农业生产、以及水资源管理等领域提供了重要支持。 数据下载后可显示详细信息。
styles
用VHDL语言设计电梯控制器.doc
管道试压报审验表、管道强度、严密性试验记录表.doc
使用springboot实现的旅游网站
所有库函数和源代码
存储介质信息消除工具应用完善的数据消除算法,严格按照BMB21-2007《涉及国家秘密的载体销毁与信息消除安全保密要求》标准,能够灵活的实现对存储介质中的数据进行完全擦除,不留痕迹,是我国各级政府、军工保密信息化建设以及各企业中不可缺少的工具。 数据一旦执行消除操作,专业的数据恢复工具也无法对其进行恢复,彻底解决用户的后顾之忧。同时不损坏存储介质,是国内先进的非暴力信息消除工具,可以有效降低用户的存储成本。可以对各种硬盘、软盘、U 盘、存储卡等进行数据粉碎,并且支持多种的磁盘分区格式,包括FAT 系列、NTFS 系列等磁盘格式进行数据销毁,确保了存储介质数据信息的安全性。 存储介质信息消除工具适用于机密级即以下涉密计算机存储介质上的信息消除,满足分级保护系统要求。 主要功能: 1. 支持单个或多个文件、目录、磁盘信息的消除。 2. 支持单个或多个磁盘剩余空间中残留信息的消除。 3. 支持搜索深度上网痕迹、文件(夹)删除痕迹、深度USB存储设备接入痕迹来确认系统中是否残留涉密信息 4. 支持清除其他多种违规外联痕迹