`
234390216
  • 浏览: 10239293 次
  • 性别: Icon_minigender_1
  • 来自: 深圳
博客专栏
A5ee55b9-a463-3d09-9c78-0c0cf33198cd
Oracle基础
浏览量:462919
Ad26f909-6440-35a9-b4e9-9aea825bd38e
springMVC介绍
浏览量:1776044
Ce363057-ae4d-3ee1-bb46-e7b51a722a4b
Mybatis简介
浏览量:1398787
Bdeb91ad-cf8a-3fe9-942a-3710073b4000
Spring整合JMS
浏览量:395164
5cbbde67-7cd5-313c-95c2-4185389601e7
Ehcache简介
浏览量:680182
Cc1c0708-ccc2-3d20-ba47-d40e04440682
Cas简介
浏览量:531179
51592fc3-854c-34f4-9eff-cb82d993ab3a
Spring Securi...
浏览量:1185114
23e1c30e-ef8c-3702-aa3c-e83277ffca91
Spring基础知识
浏览量:468885
4af1c81c-eb9d-365f-b759-07685a32156e
Spring Aop介绍
浏览量:151507
2f926891-9e7a-3ce2-a074-3acb2aaf2584
JAXB简介
浏览量:68369
社区版块
存档分类
最新评论

JAXB(五)——处理动态元素或属性

    博客分类:
  • JAXB
阅读更多

处理动态元素或属性

动态元素

假设现在有一项解析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对于动态属性的处理方式。

0
0
分享到:
评论

相关推荐

    Java动态解析XML

    如果`C`元素的`N`属性等于`F`,并且值等于`0`或`1`,则会设置一个布尔标志`flag`,表示查询方式是精确查询或模糊查询。 6. 遍历`C`元素的集合,根据`flag`值构造SQL查询语句的条件部分。如果`flag`为真,表示精确...

    用jaxb 实现 xml转java对象技术

    **XML到Java对象转换技术——基于JAXB** 在软件开发中,XML作为一种数据交换格式,广泛用于不同系统间的数据传输。然而,面对大量的XML数据,手动解析和转换为Java对象显得繁琐且效率低下。这时,Java Architecture...

    JaxbUtil.zip_jaxb

    本教程将深入探讨`JaxbUtil.zip_jaxb`中涉及的核心知识点——JAXB,以及如何使用`JaxbUtil.java`这个工具类进行XML到Java对象以及Java对象到XML的转换。 **JAXB简介** JAXB是Java平台标准版(Java SE)的一部分,它...

    xml实用技术教程—— 顾兵

    1. XML结构:XML文档由元素、属性、文本、注释和处理指令等组成。元素是XML的核心,用于定义数据结构。例如,`&lt;book&gt;`元素可以表示一本书的信息,其中可以包含子元素如`&lt;title&gt;`、`&lt;author&gt;`等。 2. XML命名规则:...

    Jaxb2 实现JavaBean与xml互转

    **JavaBean与XML互转——基于JAXB2的深度解析** 在Java开发中,XML作为一种通用的数据交换格式,被广泛用于存储和传输数据。而JavaBean则是Java对象的一种简化表示,便于封装和处理业务逻辑。当需要在Java程序中...

    jdk1.6扩展类与属性.txt

    - **API支持**:StAX提供了一个迭代式的API,可以逐个处理XML元素,而不需要一次性加载整个文档到内存中。 - **性能优化**:由于采用流式处理,StAX可以有效地处理大型XML文档,降低了内存消耗。 - **集成JAXP**:...

    xml 做数据库的 留言板 【——转——】

    2. XML解析:如何使用编程语言的库来解析和操作XML文档,例如Python的lxml或Java的JAXB。 3. 数据持久化:理解如何将XML文件作为数据存储,以及如何读取和写入这些文件。 4. 富文本编辑:了解富文本编辑器的工作原理...

    dom4j-1.6.1版本的jar

    可以通过添加、删除、替换或移动元素、属性来修改XML结构。例如,可以使用`Element.addElement()`添加新元素,`Element.removeContent()`删除所有子元素。 6. **命名空间处理**:DOM4J支持XML命名空间,这对于处理...

    XML学习指南 大部分基本的XML问题

    元素可以包含其他元素、文本或属性。属性提供了关于元素的附加信息,例如`&lt;element attr="value"&gt;`,`attr`是属性名,`value`是属性值。 XML文档必须遵循一定的规则,称为XML规范。例如,文档必须有一个根元素,...

    xml语法及例子快速入门

    XML文档的结构由元素(Element)、属性(Attribute)、文本内容(Text Content)、注释(Comment)和处理指令(Processing Instruction)等组成。以下是一些基本概念: 1. **元素(Element)**:XML文档的核心,用...

    this is lecture 5.pdf.zip_The Number_xml

    XML文档由一系列元素构成,每个元素都可以包含其他元素或文本内容。元素通过开始标签(如 `&lt;element&gt;`)和结束标签(如 `&lt;/element&gt;`)定义,并且可以有属性来提供附加信息。XML文档必须遵循严格的语法规则,如元素...

    Java中四种XML解析技术分析对比

    本篇文章将深入探讨四种主要的XML解析技术——DOM、SAX、StAX以及JAXB,并进行详细的分析与对比。 1. DOM(Document Object Model) DOM解析器将整个XML文档加载到内存中,形成一个树形结构,即DOM树。这种解析方式...

    很简单的XML 实验源代码13

    实践过程中,你可能还会接触到XML库,如Java的JAXB或DOM4J,Python的lxml库,它们能简化XML处理工作。 记得,虽然实验标题说“很简单”,但XML是一个强大的工具,深入理解和熟练应用需要时间。在实验中,不要害怕...

    XML开发技术教程

    通过学习XPath表达式,我们可以方便地查找、导航和选择XML文档中的特定元素、属性或文本,这对于数据处理和查询至关重要。 紧接着,《XSLT》(Extensible Stylesheet Language Transformations)章节将向我们展示...

    最新XML学习进阶课件

    - XML文档结构:包括XML声明、元素、属性、文本内容、注释和处理指令等基本组成元素。 - 名称空间:理解XML名称空间的概念,如何通过命名空间避免元素和属性名的冲突。 - 验证XML:介绍XML文档的结构验证,包括...

    解析xml详解及jar及源码

    此外,还可以使用第三方库,如Apache的DOM4J、JAXB或Woodstox,它们提供了额外的功能和性能优化。 总结,XML解析是Java开发中的常见任务,理解DOM、SAX和StAX的原理和使用方法是必要的技能。同时,深入研究相关jar...

    xml编程从入门到精通

    元素可以包含其他元素或文本,也可以拥有属性,如`编程指南"&gt;`。 4. **命名空间**:在XML中,为了避免元素和属性名称冲突,可以使用命名空间。命名空间通过`xmlns`属性定义,并使用前缀来引用,如`...

    java_test--/.//15

    标题 "java_test--/.//15" 和描述 "structrue_at.csv---xmlfind.java ——基础学习代码实例----115" 提供的信息表明这是一个关于Java编程的学习资源,特别是涉及XML处理的基础代码实例。我们可以从这个信息中抽取几...

    一个Jdom用法的简单例子

    - **不支持XML Schema**: 相比于其他XML库,如JAXB或StaX,JDOM在处理XML Schema时较为不便。 - **版本兼容性问题**: 不同版本的JDOM可能存在兼容性问题,升级时需要注意。 **示例代码——JDomDemo** JDomDemo示例...

    支持XML转Bean

    2. **添加JAXB注解**:为了让JAXB知道如何处理这些属性,我们需要在类和属性上添加适当的注解。对于简单的属性,我们可以使用`@XmlElement`: ```java import javax.xml.bind.annotation.XmlElement; import javax....

Global site tag (gtag.js) - Google Analytics