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

JAXB(七)——监听器

    博客分类:
  • JAXB
阅读更多

监听器

在进行marshal和unmarshal的时候JAXB为我们提供了对应的监听器,允许我们在marshal和unmarshal的过程中对当前对象做一些操作或者记录一些日志等。

marshal监听器

marshal过程中的监听器是对应的是Marshaller.Listener抽象类,其定义如下:

public static abstract class Listener {
    
    public void beforeMarshal(Object source) {

    }

    
    public void afterMarshal(Object source) {

    }
}

默认都是空实现,beforeMarshal方法用于在转换对象为XML之前回调,afterMarshal方法用于在转换对象为XML之后回调,参数source就是当前正在转换为XML的对象。监听器是通过Marshaller.setListener(Listener listener)来指定的,其会对当前Marshaller进行的对象中的每一个复杂对象转换为XML时回调。假设有下面这样的类定义:

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="root")
public class OrgHolder {
    private Org org;

    public Org getOrg() {
        return org;
    }

    public void setOrg(Org org) {
        this.org = org;
    }
    
}

@XmlAccessorType(XmlAccessType.FIELD)
public class Org {

    private String no;
    private String name;
    
    public String getNo() {
        return no;
    }
    public void setNo(String no) {
        this.no = no;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    
}

我们简单的定义如下这样的监听器,它只是简单的输出当前的动作:

public class GlobalMarshalListener extends Marshaller.Listener {

    @Override
    public void beforeMarshal(Object source) {
        System.out.println("马上要被marshal的对象是:" + source);
    }

    @Override
    public void afterMarshal(Object source) {
        System.out.println("刚刚被marshal的对象是:" + source);
    }
    
}

运行如下测试程序:

@Test
public void test() throws Exception {
    
    OrgHolder holder = this.buildOrgHolder();
    JAXBContext jaxbContext = JAXBContext.newInstance(OrgHolder.class);
    Marshaller marshaller = jaxbContext.createMarshaller();
    marshaller.setListener(new GlobalMarshalListener());//指定Listener,全局的,对当前Marshaller中所有的对象marshal都起作用
    StringWriter writer = new StringWriter();
    marshaller.marshal(holder, writer);
    
}

private OrgHolder buildOrgHolder() {
    OrgHolder holder = new OrgHolder();
    Org org = new Org();
    org.setNo("A001");
    org.setName("XXX");
    holder.setOrg(org);
    return holder;
}

我们会看到如下这样的输出:

马上要被marshal的对象是:com.elim.jaxb.OrgHolder@5577140b
马上要被marshal的对象是:com.elim.jaxb.OrgHolder@5577140b
马上要被marshal的对象是:com.elim.jaxb.Org@1c6b6478
刚刚被marshal的对象是:com.elim.jaxb.Org@1c6b6478
刚刚被marshal的对象是:com.elim.jaxb.OrgHolder@5577140b
刚刚被marshal的对象是:com.elim.jaxb.OrgHolder@5577140b

从输出中我们可以看到根对应的marshal过程中对应的监听器方法被调用了两次,所以需要确保监听器中进行的操作是幂等的。

unmarshal监听器

unmarshal过程中设置的监听器是Unmarshaller.Listener,其定义如下:

public static abstract class Listener {

    public void beforeUnmarshal(Object target, Object parent) {

    }

    public void afterUnmarshal(Object target, Object parent) {

    }

}

beforeUnmarshal方法将在当前对象被实例化,但是在XML转换为对象前调用,afterUnmarshal方法将在XML转换为对象后调用。参数target是当前正在被unmarshal的对象,parent是持有当前对象的引用的对象,即所谓的父对象。unmarshal过程中使用的Listener的示例如下:

public class GlobalUnmarshalListener extends Unmarshaller.Listener {

    @Override
    public void beforeUnmarshal(Object target, Object parent) {
        System.out.println("马上要被unmarshal的对象是:" + target + ",该对象的父级对象是:" + parent);
    }

    @Override
    public void afterUnmarshal(Object target, Object parent) {
        System.out.println("刚刚被unmarshal的对象是:" + target + ",该对象的父级对象是:" + parent);
    }
    
}

测试代码如下:

@Test
public void test() throws Exception {
    
    OrgHolder holder = this.buildOrgHolder();
    JAXBContext jaxbContext = JAXBContext.newInstance(OrgHolder.class);
    Marshaller marshaller = jaxbContext.createMarshaller();
    StringWriter writer = new StringWriter();
    marshaller.marshal(holder, writer);
    
    Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
    unmarshaller.setListener(new GlobalUnmarshalListener());
    unmarshaller.unmarshal(new StringReader(writer.toString()));
    
}

输出如下:

马上要被unmarshal的对象是:com.elim.jaxb.OrgHolder@26ba2a48,该对象的父级对象是:null
马上要被unmarshal的对象是:com.elim.jaxb.Org@5f2050f6,该对象的父级对象是:com.elim.jaxb.OrgHolder@26ba2a48
刚刚被unmarshal的对象是:com.elim.jaxb.Org@5f2050f6,该对象的父级对象是:com.elim.jaxb.OrgHolder@26ba2a48
刚刚被unmarshal的对象是:com.elim.jaxb.OrgHolder@26ba2a48,该对象的父级对象是:null

上面的示例中虽然我们实现的监听器只是简单的输出了一些信息,但实际上我们可以使用它们来辅助XML和Java相互转换的过程,比如unmarshal时unmarshal的结果可能是org.w3c.dom.Element类型的对象,但我们可以通过Unmarshaller.Listener的afterUnmarshal方法把它变为一个我们最终需要的对象。具体的应用场景就要看具体的业务需要了。

实例回调方法

Unmarshaller.Listener和Marshaller.Listener作用的都是当前unmarshal或marshal中对应的所有的对象,对每个对象进行转换时都将进行调用,如果我们只是希望对某个具体的对象进行转换时进行监听,则可以使用实例级别的监听。 在使用JAXB进行对象和XML之间的相互转换时,如果对应的类按照JAXB的规范定义了一些回调方法,JAXB会在进行操作时调用对应的回调方法,对应的回调方法一共有四个。

  • beforeMarshal(Marshaller marshaller):在对当前对象marshal前调用
  • afterMarshal(Marshaller marshaller):在对当前对象marshal后调用
  • beforeUnmarshal(Unmarshaller unmarshaller, Object parent):在对当前对象进行unmarshal前调用
  • afterUnmarshal(Unmarshaller unmarshaller, Object parent):在对当前对象进行unmarshal后调用

需要注意的是这些方法定义必须与规定的一致:方法名必须一致;方法参数类型、个数和顺序必须一致;至于是否有抛出异常这些是不影响的。这些回调方法不需要四个都定义,可以只定义你感兴趣的回调方法。下面是一个Org类的定义,其中就定义对应的四个回调方法,

@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name="org")
public static class Org {
    private String no;
    private String name;
    public String getNo() {
        return no;
    }
    public void setNo(String no) {
        this.no = no;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    
    /**
     * 方法名必须是beforeMarshal,必须只接收一个Marshaller类型的参数,至于是否抛出异常JVM是不管的
     * @param marshaller
     */
    public void beforeMarshal(Marshaller marshaller) {
        System.out.println("马上要marshal本对象了");
    }
    
    public void afterMarshal(Marshaller marshaller) {
        System.out.println("该对象已经被marshal了");
    }
    
    public void beforeUnmarshal(Unmarshaller unmarshaller, Object parent) {
        System.out.println("马上要unmarshal该对象了,持有该对象的父级对象是:" + parent);
    }
    
    public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
        System.out.println("该对象已经unmarshal完成了,持有该对象的父级对象是:" + parent);
    }
    
}

进行测试如下:

@Test
public void test2() throws Exception {
    
    Org org = this.buildOrg();
    JAXBContext jaxbContext = JAXBContext.newInstance(Org.class);
    Marshaller marshaller = jaxbContext.createMarshaller();
    StringWriter writer = new StringWriter();
    marshaller.marshal(org, writer);

    System.out.println("--------------分界线--------------");

    Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
    unmarshaller.unmarshal(new StringReader(writer.toString()));
    
}

private Org buildOrg() {
    Org org = new Org();
    org.setNo("A001");
    org.setName("XXX");
    return org;
}

输出如下:

马上要marshal本对象了
马上要marshal本对象了
该对象已经被marshal了
该对象已经被marshal了
--------------分界线--------------
马上要unmarshal该对象了,持有该对象的父级对象是:null
该对象已经unmarshal完成了,持有该对象的父级对象是:null

跟全局的Marshaller.Listener一样,marshal相关的回调方法会被回调两次。

笔者曾经遇到过这样一个需求,一段XML节点下面的内容,有的时候是一段JSON字符串,有的时候是一段XML,类似于下面这样。data节点下面的内容有的时候是一段XML,有的时候是一段JSON,当然了实际情况下它们表示的内容不是一样的,笔者这里只是为了更好的说明问题才把它们弄成一样的。

<root><data>{'id': '1', 'name': 'ABCDE'}</data></root>
<root><data><user id="1"><name>ABCDE</name></user></data></root>

这种场景下就可以通过使用实例级别的unmarshal回调方法实现把JSON字符串和XML都转换为需要的User对象。那这个时候我们的代码可以是如下这样,其中的setData()只是用来接收最原始的XML内容的,即作为data节点的Java映射。afterUnmarshal会在unmarshal之后把data节点的内容根据内容的不同分别转换为对应的User对象,然后把它赋予realData属性,之后就可以通过getRealData()方法读取到对应的User对象,而不用管底层的User对象到底是如何转换来的。

@XmlRootElement(name="root")
public static class RootObj {
    
    private Element data;
    
    private User realData;
    
    @XmlAnyElement
    public void setData(Element data) {
        this.data = data;
    }
    
    public User getRealData() {
        return this.realData;
    }
    
    public void afterUnmarshal(Unmarshaller unmarshaller, Object parent) {
        if (this.data == null) {
            return;
        }
        Node node = this.data.getFirstChild();
        if (node.getNodeType() == Node.TEXT_NODE) {//直接是文本节点的是JSON
            String jsonData = ((Text)node).getData();
            this.realData = JSON.parseObject(jsonData, User.class);
        } else {
            Element ele = (Element) node;
            String id = ele.getAttribute("id");
            String name = ((Text)ele.getFirstChild().getFirstChild()).getData();
            User user = new User();
            user.setId(id);
            user.setName(name);
            this.realData = user;
        }
    }
    
}

测试代码如下:

@Test
public void testListener() throws Exception {
    JAXBContext jaxbContext = JAXBContext.newInstance(RootObj.class);
    String xml1 = "<root><data>{'id': '1', 'name': 'ABCDE'}</data></root>";
    String xml2 = "<root><data><user id=\"1\"><name>ABCDE</name></user></data></root>";
    Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
    RootObj rootObj = (RootObj) unmarshaller.unmarshal(new StringReader(xml1));
    RootObj rootObj2 = (RootObj) unmarshaller.unmarshal(new StringReader(xml2));
    Assert.assertTrue(rootObj.getRealData().equals(rootObj2.getRealData()));
}

(完)

0
0
分享到:
评论

相关推荐

    jdk1.6扩展类与属性.txt

    - **托盘图标操作**:可以设置托盘图标的图像、提示信息、监听器等属性,并可以通过添加菜单项来实现托盘图标的交互功能。 - **自动启动**:可以在托盘图标上设置一个URL,使得应用程序能够在系统启动时自动打开特定...

    CoreJava上下册

    6. **Swing GUI编程**:介绍如何创建图形用户界面(GUI),使用JFrame,JButton,JLabel,JTextArea等组件,以及事件监听器的实现。 第二卷——《Core Java 2 Volume II 7th Edition》: 这一卷则更偏向于高级主题...

    xfire整合spring发布web services

    在`web.xml`中,我们需要配置Spring的`ContextLoaderListener`和`IntrospectorCleanupListener`监听器,以及Xfire的Servlet,以启动和管理Spring容器以及Xfire服务。 在`web.xml`的Spring配置部分,我们设置了`...

    dom4j-1.6.1版本的jar

    7. **事件处理**:DOM4J支持SAX事件处理,可以注册事件监听器来响应XML解析过程中的各种事件,如开始文档、结束文档、开始元素和结束元素等。 8. **性能优化**:DOM4J设计时考虑了性能,提供了内存管理和缓存机制,...

    java api规范

    12. **JAXB 2.0**:Java Architecture for XML Binding(JAXB)2.0版本提供了更强大的XML和Java对象之间的映射功能,简化了XML数据的序列化和反序列化。 以上只是Java API 6中部分关键知识点的概述,实际上,这个...

    org.springframework.web-3.0.0.RC3.jar

    2. **RequestContextListener**:这个监听器帮助初始化和清理Web上下文,确保每个请求都有一个独立的上下文环境。 四、Spring Web服务 除了MVC,Spring Web还支持Web服务的创建和消费。在3.0.0.RC3版本中,Spring...

    java规则引擎 -- Drools

    开发者可以通过监听器或回调函数获取规则执行的结果。 关于Drools的源码,由于未提供具体文件,这里简单说明一下,Drools的源码是用Java编写的,遵循Maven构建系统。源码分析可以帮助我们理解其内部实现机制,如...

    学生信息管理系统

    《学生信息管理系统——基于Java和XML的实现》 学生信息管理系统是教育机构日常管理工作中不可或缺的一部分,它能够高效地存储、检索和管理大量的学生数据。本系统采用XML作为数据库,结合Java编程技术,构建了一个...

    AXIS2 入门文档

    - **Listeners/Observers**:定义监听器或观察者。 - **服务配置**:针对具体服务的配置,通常包含在每个服务的目录下的部署文件中。 - **Module配置**:用于配置特定功能模块的行为,例如安全模块、日志记录模块等...

    使用XFire发布WebService

    XFire是Apache CXF的前身,它提供了全面的WS-*支持,包括SOAP、WSDL、UDDI、MTOM等标准,以及Java到XML绑定(如JAXB)和数据绑定框架(如Aegis)。本教程将深入探讨如何使用XFire来发布和调用Web服务。 【描述】: ...

    Spring.3.x企业应用开发实战(完整版).part2

    12.2.4 添加Hibernate事件监听器 12.2.5 使用原生Hibernate API 12.2.6 使用注解配置 12.2.7 事务处理 12.2.8 延迟加载的问题 12.3 在Spring中使用myBatis 12.3.1 配置SqlMapClient 12.3.2 在Spring配置myBatis ...

    Spring3.x企业应用开发实战(完整版) part1

    12.2.4 添加Hibernate事件监听器 12.2.5 使用原生Hibernate API 12.2.6 使用注解配置 12.2.7 事务处理 12.2.8 延迟加载的问题 12.3 在Spring中使用myBatis 12.3.1 配置SqlMapClient 12.3.2 在Spring配置myBatis ...

    java6.0源码-wqmanager:水质经理

    《深入解析Java 6.0源码:以wqmanager——水质经理为例》 Java作为全球最广泛使用的编程语言之一,其源码是开发者学习、理解和优化代码的基础。本篇文章将深入探讨Java 6.0的源码,并以“wqmanager”项目为例,这个...

    Map-Maker:MapEditor的修订版

    《Map-Maker:MapEditor的修订版——深入解析Java地图编辑器开发》 Map-Maker是MapEditor的一个修订版,专为地图制作爱好者和游戏开发者设计。它利用Java编程语言的强大功能,提供了一种直观、易用的界面,用于创建...

Global site tag (gtag.js) - Google Analytics