JiBX 的实操

本文介绍了JiBX作为XML数据绑定框架的基本概念和优势,包括转换效率高、配置简单等。通过BECL技术动态修改类文件,实现XML与Java对象的映射。文章详细讲解了使用Ant工具生成绑定文件和绑定关系的过程,以及测试示例,展示了JiBX的Unmarshal和Marshal功能。最后提到了一些可能出现的错误及解决方案,但不支持集合和枚举属性的绑定问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

介绍

JiBX是一款非常优秀的XML(Extensible Markup Language)数据绑定框架。它提供灵活的绑定映射文件,实现数据对象与XML文件之间的转换,并不需要修改既有的Java类。并且转换效率极高。
XML 已经成为目前程序开发配置的重要组成部分了,JiBX 操作XML 的优点有:
(1)转换效率高
(2)配置绑定文件简单
(3)不需要操作xpath文件
(4)不需要写属性的get/set 方法
(5)对象属性名与XML文件element名可以不同等

基本概念

使用JiBX 绑定XML文档与Java对象需要分两步走:
(1)绑定XML文件,也就是映射XML文件与Java对象之间的对应关系
(2)在运行时,实现XML文件与Java实例之间的互相转换。我们要的就是能够 实现 这个功能。

BECL 介绍

在运行程序之前,需要先配置绑定文件并进行绑定,在绑定过程中它将会动态地修改程序中相应的class文件,主要是生成对应对象实例的方法和添加被绑定标记的属性JiBX_bindingList。 使用的技术是BECL(Byte Code Engineering Library),BCEL 是Apache Software Foundation的Jakarta 项目的一部分,也是目前Java classworking 最广泛使用的一种框架,它可以让我们深入JVM汇编语言进行操作。

JiBX 基础概念

Unmarshal 数据分解,将XML文件转换成Java对象
Marshal 数据编排 将Java对象编排成规范的XML文件。
JiBX 在 Unmarshal/Marshal 上如此高效,这要归功于使用了XPPXml Pull Parsing)技术,而不是使用基于树形方式,将整个文档写入内存,然后进行操作的DOM。也不是使用基于事件流的SAX。XPP使用的是不断增加的数据流处理方式,同时允许在解析XML文件时中断。

开发

jar 依赖

BECL

       <dependency>
            <groupId>org.apache.bcel</groupId>
            <artifactId>bcel</artifactId>
            <version>6.7.0</version>
        </dependency>

JiBX

        <dependency>
            <groupId>org.jibx</groupId>
            <artifactId>jibx-run</artifactId>
            <version>1.4.2</version>
        </dependency>
        <dependency>
            <groupId>org.jibx</groupId>
            <artifactId>jibx-extras</artifactId>
            <version>1.4.2</version>
        </dependency>
        <dependency>
            <groupId>org.jibx</groupId>
            <artifactId>jibx-bind</artifactId>
            <version>1.4.2</version>
        </dependency>
        <dependency>
            <groupId>org.jibx</groupId>
            <artifactId>jibx-tools</artifactId>
            <version>1.4.2</version>
        </dependency>
        <dependency>
            <groupId>org.jibx</groupId>
            <artifactId>jibx-schema</artifactId>
            <version>1.4.2</version>
        </dependency>

POJO 实体类

Order

public class Order {
    private long orderNumber;
    private Customer customer;

    /**
     * Billing address information
     */
    private Address bilTo;

    private String shipping;

    /**
     * Shipping address information. If missing ,the billing address is also
     * used as the shipping address.
     */
    private Address shipTo;

    private Float total;

    public long getOrderNumber() {
        return orderNumber;
    }

    public void setOrderNumber(long orderNumber) {
        this.orderNumber = orderNumber;
    }

    public Customer getCustomer() {
        return customer;
    }

    public void setCustomer(Customer customer) {
        this.customer = customer;
    }

    public Address getBilTo() {
        return bilTo;
    }

    public void setBilTo(Address bilTo) {
        this.bilTo = bilTo;
    }

    public String getShipping() {
        return shipping;
    }

    public void setShipping(String shipping) {
        this.shipping = shipping;
    }

    public Address getShipTo() {
        return shipTo;
    }

    public void setShipTo(Address shipTo) {
        this.shipTo = shipTo;
    }

    public Float getTotal() {
        return total;
    }

    public void setTotal(Float total) {
        this.total = total;
    }

    @Override
    public String toString() {
        return "Order{" +
                "orderNumber=" + orderNumber +
                ", customer=" + customer +
                ", bilTo=" + bilTo +
                ", shipping=" + shipping +
                ", shipTo=" + shipTo +
                ", total=" + total +
                '}';
    }
}

Address

public class Address {
    /**
     * First line of street information (required).
     */
    private String street1;

    /**
     * Second line of street information (optional).
     */
    private String street2;

    private String city;

    /**
     * State abbreviation (required for the U.S. and Canada, optional otherwise )
     */

    private String state;

    /**
     * Postal code (required for the U.S. and Canada ,optional otherwise ).
     */
    private String postCode;

    /**
     * Country name (optional ,U.S. assumed if not supplied).
     */
    private String country;


    public String getStreet1() {
        return street1;
    }

    public void setStreet1(String street1) {
        this.street1 = street1;
    }

    public String getStreet2() {
        return street2;
    }

    public void setStreet2(String street2) {
        this.street2 = street2;
    }

    public String getCity() {
        return city;
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getState() {
        return state;
    }

    public void setState(String state) {
        this.state = state;
    }

    public String getPostCode() {
        return postCode;
    }

    public void setPostCode(String postCode) {
        this.postCode = postCode;
    }

    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    @Override
    public String toString() {
        return "Address{" +
                "street1='" + street1 + '\'' +
                ", street2='" + street2 + '\'' +
                ", city='" + city + '\'' +
                ", state='" + state + '\'' +
                ", postCode='" + postCode + '\'' +
                ", country='" + country + '\'' +
                '}';
    }
}

Customer

public class Customer {
    private long customerNumber;
    /**
     * Personal name
     */
    private String firstName;

    /**
     * Family name .
     */
    private String lastName;

    /**
     * Middle name(s) if any.
     */
    private List<String> middleNames;


    public long getCustomerNumber() {
        return customerNumber;
    }

    public void setCustomerNumber(long customerNumber) {
        this.customerNumber = customerNumber;
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public List<String> getMiddleNames() {
        return middleNames;
    }

    public void setMiddleNames(List<String> middleNames) {
        this.middleNames = middleNames;
    }

    @Override
    public String toString() {
        return "Customer{" +
                "customerNumber=" + customerNumber +
                ", firstName='" + firstName + '\'' +
                ", lastName='" + lastName + '\'' +
                ", middleNames=" + middleNames +
                '}';
    }
}

Shipping

public class Shipping {
    public final static String STANDARD_MAIL="STANDARD_MAIL";
    public final static String PRIORITY_MAIL="PRIORITY_MAIL";
    public final static String INTERNATIONAL_MAIL="StringINTERNATIONAL_MAIL";
    public final static String DOMESTIC_EXPRESS="DOMESTIC_EXPRESS";
    public final static String INTERNATIONAL_EXPRESS="INTERNATIONAL_EXPRESS";
}

OrderFactory 工厂类

public class OrderFactory {
    public static Order create(int number){
        Order order=new Order();
        //orderNumber
        order.setOrderNumber(number);
        //total
        order.setTotal(9999.999f);
        //customer
        Customer customer=new Customer();
        customer.setFirstName("echo");
        customer.setLastName("tong");
        List<String>list=new ArrayList<>();
        list.add("666");
       // list.add("555");
        customer.setCustomerNumber(123);
        //customer.setMiddleNames(list);
        order.setCustomer(customer);

        //billTo
        Address billTo=new Address();
        billTo.setStreet1("西乡大道");
        billTo.setCity("深圳市");
        billTo.setState("广东省");
        billTo.setPostCode("123321");
        billTo.setCountry("中国");
        order.setBilTo(billTo);
        //shipping
        order.setShipping(Shipping.INTERNATIONAL_MAIL);
        //shipTo
        Address shipTo=new Address();
        shipTo.setStreet1("西乡大道");
        shipTo.setCity("深圳市");
        shipTo.setState("广东省");
        shipTo.setPostCode("123321");
        shipTo.setCountry("中国");
        order.setShipTo(shipTo);


        return order;
    }
}

使用ant 来生成XML和POJO对象的绑定关系

idea 使用ant

idea 里面有自带的ant 工具。打开方式 View —Tool Windows–Ant
在这里插入图片描述
打开后,右上角就会有一个蚂蚁图标。如下图
在这里插入图片描述

ant 脚本 build.xml

需要修改的地方主要有两个:
一个是 jibx和bcel 依赖的路径。
另一个是POJO实体类的全路径

<?xml version="1.0" encoding="utf-8"?>
<project default="main" basedir=".">
    <path id="classpath">
        <dirset dir="${basedir}/target/classes" />
        <!--由于不需要编译单元测试代码,就注掉了下面的内容-->
        <!--<dirset dir="${basedir}/target/test-classes" />-->

        <!--下面目录为本地maven仓库的jibx和bcel的jar包的绝对路径  同学们也可以采用相对路径-->
        <fileset dir="D:/ProgramFiles/apache-maven-3.6.3/conf/repository/org/jibx/jibx-bind/1.4.2/" includes="*.jar" />
        <fileset dir="D:/ProgramFiles/apache-maven-3.6.3/conf/repository/org/jibx/jibx-run/1.4.2/" includes="*.jar" />
        <fileset dir="D:/ProgramFiles/apache-maven-3.6.3/conf/repository/org/apache/bcel/bcel/6.0/" includes="*.jar" />
    </path>
    <!--这个是主任务 , depends 依赖下面写的三个分任务 -->
    <target name="main" depends="compile,bindgen,bind" description="Main target" />
    <target name="compile" description="Compilation target">
        <echo>Building file.</echo>
        <!--相当于运行 javac命令进行源码编译-->
        <javac srcdir="${basedir}/src/main/java" destdir="${basedir}/target/classes" includeantruntime="true" />
    </target>
    <target name="bindgen">
        <echo message="Running BindGen tool" />
        <!--
        相当于运行Java命令生成binding.xml文件 类似于网上说的如下命令 ->
        java -cp ..libx-tools.jar ..BindGen -t 生成文件保存地址 -v 需要绑定文件的class文件 完整包名.类名
        -->
        <java classpathref="classpath" fork="true"  classname="org.jibx.binding.BindingGenerator">
            <!-- 此处写需要生成映射文件的实体类的全类名-->


            <arg value="echo.cn.http.xml.pojo.Address" />
            <arg value="echo.cn.http.xml.pojo.Customer" />
            <arg value="echo.cn.http.xml.pojo.Order" />
          <arg value="echo.cn.http.xml.pojo.Shipping" />
        </java>
    </target>
    <target name="bind">
        <!--将实体类的class和xml映射文件进行绑定-->
        <echo message="Running bind" />
        <taskdef name="bind" classname="org.jibx.binding.ant.CompileTask">
            <classpath refid="classpath">

            </classpath>
        </taskdef>
        <bind binding="${basedir}/binding.xml">
            <classpath refid="classpath"/>
        </bind>
    </target>
</project>

建议放在根目录下,先和我们保持一致,熟练后可以自定义修改。位置如下图所示:
在这里插入图片描述

ant 添加 build.xml

首先 在ant 里面添加build.xml.
然后依次执行 bindgen和bind命令。其中bindgen命令会生成binding.xml文件。即实体类和XML的绑定关系 的描述文件。
bind 命令 会动态的修改实例类的class文件,在里面添加对应的 JiBX_bindingList 属性值,并动态实现 IUnmarshallable和IMarshallable接口,同时会生成实体类的access文件等。
在这里插入图片描述
在这里插入图片描述

binding.xml
<?xml version="1.0" encoding="UTF-8"?>
<binding value-style="attribute">
  <mapping class="echo.cn.http.xml.pojo.Address" name="address">
    <value style="element" name="street1" field="street1" usage="optional"/>
    <value style="element" name="street2" field="street2" usage="optional"/>
    <value style="element" name="city" field="city" usage="optional"/>
    <value style="element" name="state" field="state" usage="optional"/>
    <value style="element" name="post-code" field="postCode" usage="optional"/>
    <value style="element" name="country" field="country" usage="optional"/>
  </mapping>
  <mapping class="echo.cn.http.xml.pojo.Customer" name="customer">
    <value name="customer-number" field="customerNumber"/>
    <value style="element" name="first-name" field="firstName" usage="optional"/>
    <value style="element" name="last-name" field="lastName" usage="optional"/>
    <collection field="middleNames" usage="optional" factory="org.jibx.runtime.Utility.arrayListFactory"/>
  </mapping>
  <mapping class="echo.cn.http.xml.pojo.Order" name="order">
    <value name="order-number" field="orderNumber"/>
    <structure field="customer" usage="optional"/>
    <structure field="bilTo" usage="optional"/>
    <value style="element" name="shipping" field="shipping" usage="optional"/>
    <structure field="shipTo" usage="optional"/>
    <value name="total" field="total" usage="optional"/>
  </mapping>
  <mapping class="echo.cn.http.xml.pojo.Shipping" name="shipping"/>
</binding>

报错问题解决

bindgen 命令可能会报路径不存在。此时需要先编译,生成target目录。可以用Maven的compile命令先执行生成对应的目录即可。

测试

前面 铺垫工作做好了后,我们就来测试以下。

TestOrder

public class TestOrder {
    private IBindingFactory factory=null;

    private StringWriter writer=null;

    private StringReader reader=null;

    private final static String CHARSET_NAME="UTF-8";

    private String encode2Xml(Order order) throws JiBXException, IOException {
       
        factory= BindingDirectory.getFactory(Order.class);
        writer=new StringWriter();
        IMarshallingContext context=factory.createMarshallingContext();
        context.setIndent(2);
        // marshal 将实体类编码成XML字符
        context.marshalDocument(order,CHARSET_NAME,null,writer);
        String xmlStr=writer.toString();
        writer.close();;
        System.out.println(xmlStr.toString());
        return xmlStr;
    }


    private Order decode2Order(String xmlBody) throws JiBXException {
        reader =new StringReader(xmlBody);
       
        IUnmarshallingContext context = factory.createUnmarshallingContext();
        // unmarshalDocument 将XML 字符解码为POJO实体
        Order order=(Order) context.unmarshalDocument(reader);
        return  order;
    }

    public static void main(String[] args) throws JiBXException, IOException {
        TestOrder testOrder=new TestOrder();
        Order order= OrderFactory.create(123);
        String body=testOrder.encode2Xml(order);
        Order order1=testOrder.decode2Order(body);
        System.out.println(order1);
    }

}

测试结果 如图:

在这里插入图片描述
说明 JiBX Unmarshal 数据分解 和 Marshal 数据编排 成功了。

错误解决

如果报缺少 JiBX_bindingList 。 说明之前的ant bind 命令执行没有成功。需要重新执行bind 命令。

Bug遗留

目前这个Order 实体里面有 Float ,String 和其他普通POJO 属性。 JiBX 是支持的。也是可用供大家学习实操的。
但是 不支持 集合属性和 枚举属性。
集合属性报 java.lang.String cannot be cast to org.jibx.runtime.IMarshallable 异常。 大家可以在Customer 给middleNames 属性赋值试下即可。
枚举属性报 该枚举没有可用的构造方法。大家可以将 Shipping 改为enum 试下。

如果有大神知道如何解决这两个问题,不胜感激。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

echoecho_tongtong

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值