介绍

我重新发现了Java提供给大众的库。 当我第一次阅读该规范时,我很困惑,以为我需要所有这些特殊工具来实现。 我最近发现,只需要一些注释和一个POJO。

杰克斯

JAXB代表XML绑定的Java体系结构。 这种体系结构允许开发人员将来自类的数据转换为XML表示形式。 这称为编组。 该体系结构还允许开发人员逆转将XML表示转换为类的过程。 这称为解组。 有一些工具可以从XML Schema文件创建Java类。 该工具称为xjc。 还有另一种使用schemagen创建xsd文件的工具。

编组

编组和解组在Java中发生了很多地方。 我首先接触到的是RMI。 对象被发送用作远程方法调用的参数,因此名称为“远程方法调用(RMI)”。 它发生的另一个地方是将对象写入流。 实现此功能的流是ObjectOutputStream和ObjectInputStream。 发生的另一个地方是ORM类。 当然,另一种方式是编写实例的XML表示。 想要编组的类需要实现Serializable,并且其所有成员属性也都需要实现Serializable,但通过JAXB的类除外。 可序列化是标记接口。 它没有实现的方法,但是它表明可以对类进行序列化或编组。 被编组的对象的数据已采用某种持久化方式。 未编组对象的数据已从持久状态读取并与类连接。 这使得类路径非常重要。 有趣的是,类路径中的有效条目是http:// ip:port / path / to / jar 。 我想有些组织可以通过集中化jar文件来使用此功能,而最新版本仅需下载即可。

我用maven和spring来做这个例子。 原因不是使它更加复杂,而是使代码更清晰地阅读和更专注于使用我所展示的技术。 pom.xml文件中的依赖项如下:

<dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>

    <dependency>
      <groupId>com.sun.xml.bind</groupId>
      <artifactId>jaxb-impl</artifactId>
      <version>2.2.8-b01</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>3.2.3.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>3.2.3.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>3.2.3.RELEASE</version>
      </dependency>

  </dependencies>

JAXB的妙处在于它使用POJO。 Contact.java是三个集合中的中心POJO类。

package org.mathison.jaxb.beans;

import java.util.List;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlElementWrapper;
import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement
@XmlAccessorType(XmlAccessType.FIELD)
public class Contact {
    private String lastName;
    private String firstName;
    private String middleName;
    private String jobTitle;

    @XmlElementWrapper(name = "addresses")
    @XmlElement(name = "address")
    private List<Address> addresses;

    @XmlElementWrapper(name = "phone-numbers")
    @XmlElement(name = "phone-number")
    private List<PhoneNumber> numbers;

    public String getLastName() {
        return lastName;
    }

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

    public String getFirstName() {
        return firstName;
    }

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

    public String getMiddleName() {
        return middleName;
    }

    public void setMiddleName(String middleName) {
        this.middleName = middleName;
    }

    public String getJobTitle() {
        return jobTitle;
    }

    public void setJobTitle(String jobTitle) {
        this.jobTitle = jobTitle;
    }

    public List<Address> getAddresses() {
        return addresses;
    }

    public void setAddresses(List<Address> addresses) {
        this.addresses = addresses;
    }

    public List<PhoneNumber> getNumbers() {
        return numbers;
    }

    public void setNumbers(List<PhoneNumber> numbers) {
        this.numbers = numbers;
    }

    @Override
    public String toString() {
        return "Contact{" + "lastName=" + lastName + ", firstName=" 
                          + firstName + ", middleName=" + middleName 
                          + ", jobTitle=" + jobTitle + ", addresses=" 
                          + addresses + ", numbers=" + numbers + '}';
    }

    @Override
    public int hashCode() {
        int hash = 3;

        hash = 23 * hash + (this.lastName != null ? 
                            this.lastName.hashCode() : 0);

        hash = 23 * hash + (this.firstName != null ? 
                            this.firstName.hashCode() : 0);

        hash = 23 * hash + (this.middleName != null ? 
                            this.middleName.hashCode() : 0);

        hash = 23 * hash + (this.jobTitle != null ? 
                            this.jobTitle.hashCode() : 0);

        hash = 23 * hash + (this.addresses != null ? 
                            this.addresses.hashCode() : 0);

        hash = 23 * hash + (this.numbers != null ?
                            this.numbers.hashCode() : 0);

        return hash;
    }

    @Override
    public boolean equals(Object obj) {

        if (obj == null) {
            return false;
        }

        if (getClass() != obj.getClass()) {
            return false;
        }

        final Contact other = (Contact) obj;

        if ((this.lastName == null) ? (other.lastName != null) :
             !this.lastName.equals(other.lastName)) {

            return false;
        }

        if ((this.firstName == null) ? (other.firstName != null) : 
             !this.firstName.equals(other.firstName)) {
            return false;
        }

        if ((this.middleName == null) ? (other.middleName != null) : 
             !this.middleName.equals(other.middleName)) {
            return false;
        }

        if ((this.jobTitle == null) ? (other.jobTitle != null) : 
             !this.jobTitle.equals(other.jobTitle)) {
            return false;
        }

        if(!listEquals(this.addresses, other.addresses)) {
            return false;
        }

        if(!listEquals(this.numbers, other.numbers)) {
            return false;
        }

        return true;

    }

    private boolean listEquals(List first, List second) {
        for(Object o: first) {
            if(!second.contains(o)) {
                return false;
            }
        }

        return true;
    }

}

要看的主要部分是注释。 @XmlRootElement定义这是一个类的开始。 @XmlAccessorType(XmlAccessType.FIELD)告诉体系结构,这些字段将用于定义xml中的元素。 注释也可以放在吸气剂上。 如果未使用注释,则JAXB会混淆使用哪个注释。 对于存在列表的实例,@XmlElementWrapper用来告诉JAXB外部标签将是什么。 例如,有一个地址列表。 包装器采用名为“ name”的参数,并用“ addresss”填充。 呈现XML时,将在地址集合周围包裹标签“ addresses”。 如果要更改属性的标记,则使用@XmlElement批注。 回到我们的地址列表,注释将地址列表重新定义为“ address”。 这将导致每个地址对象都具有“地址”标签,而不是已经占用的“地址”标签。 数字使用相同的模式。 其余的属性将具有与它们的名称匹配的标签。 例如,lastName将变成标签“ lastName”。 其他两个POJO(电话号码.java和地址.java)具有公共枚举类。 这是PhoneNumber.java:

package org.mathison.jaxb.beans;

import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlType;

@XmlRootElement
public class PhoneNumber {

    @XmlType(name="phone-type")
    public enum Type {
        HOME,
        WORK,
        HOME_FAX,
        WORK_FAX;
    }

    private Type type;
    private String number;

    public Type getType() {
        return type;
    }

    public void setType(Type type) {
        this.type = type;
    }

    public String getNumber() {
        return number;
    }

    public void setNumber(String number) {
        this.number = number;
    }

    @Override
    public String toString() {
        return "PhoneNumber{" + "type=" + type + ", number=" + number + '}';
    }

    @Override
    public int hashCode() {
        int hash = 7;

        hash = 37 * hash + (this.type != null ? this.type.hashCode() : 0);
        hash = 37 * hash + (this.number != null ? 
                            this.number.hashCode() : 0);

        return hash;

    }

    @Override

    public boolean equals(Object obj) {

        if (obj == null) {
            return false;
        }

        if (getClass() != obj.getClass()) {
            return false;
        }

        final PhoneNumber other = (PhoneNumber) obj;

        if (this.type != other.type) {
            return false;
        }

        if ((this.number == null) ? (other.number != null) : 
             !this.number.equals(other.number)) {
            return false;
        }

        return true;
    }

}

注释的注释为@XmlType。 这告诉JAXB一类有限数量的值。 它带有一个名称参数。 最后一个POJO还使用@XmlType定义其公共枚举类。 可以在Address.java中找到它。

全部放在一起

有了所有这些注释和类定义,是时候将所有这些放到一个主类中了。 这是App.java,主要类:

package org.mathison.jaxb.app;

import java.io.StringReader;
import java.io.StringWriter;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.Marshaller;
import javax.xml.bind.Unmarshaller;
import org.mathison.jaxb.beans.Contact;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.GenericXmlApplicationContext;

public class App 
{

    public static void main( String[] args )
    {

        ApplicationContext cxt = new GenericXmlApplicationContext("jaxb.xml");
        Contact contact = cxt.getBean("contact", Contact.class);
        StringWriter writer = new StringWriter();

        try {
            JAXBContext context = JAXBContext.newInstance(Contact.class);

            //create xml from an instance from Contact

            Marshaller m = context.createMarshaller();
            m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
            m.marshal(contact, writer);

            String xml = writer.getBuffer().toString();

            System.out.println(xml);

            //Take xml to Contact

            StringReader reader = new StringReader(xml);
            Unmarshaller u = context.createUnmarshaller();

            Contact fromXml = (Contact)u.unmarshal(reader);

            System.out.println("Are the instances equivalent: " + 
                                contact.equals(fromXml));

        } catch(Exception e){
            e.printStackTrace();
        }
    }
}

首先,从ApplicationContext检索联系人实例。 其次,以Contact类作为根类创建JAXBContext实例。 上下文将分析类结构,并创建可以封送或拆封Contact,Address和PhoneNumber类的上下文。 在下一部分中,从JAXBContext创建一个编组器。 Marshaller.JAXB_FORMATTED_OUTPUT属性设置为true。 这将创建一个格式化的XML输出。 如果未设置该属性,则XML将作为一行文本出现。 调用编组器进行编组联系并将其写入StringWriter。 然后将XML打印到System.out。 输出应如下所示:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<contact>

    <lastName>Mathison</lastName>
    <firstName>Daryl</firstName>
    <middleName>Bob</middleName>
    <jobTitle>Developer</jobTitle>
    <addresses>
        <address>
            <addressLine>123 Willow View</addressLine>
            <city>Cibolo</city>
            <state>TX</state>
            <type>HOME</type>
            <zipCode>78228</zipCode>
        </address>
        <address>
            <addressLine>411 Grieg</addressLine>
            <city>San Antonio</city>
            <state>TX</state>
            <type>WORK</type>
            <zipCode>78228</zipCode>
        </address>
    </addresses>

    <phone-numbers>
        <phone-number>
            <number>210-123-4567</number>
            <type>WORK</type>
        </phone-number>

        <phone-number>
            <number>210-345-1111</number>
            <type>HOME</type>
        </phone-number>
    </phone-numbers>

</contact>

在下一部分中,将xml与其数据一起解组到Contact实例中。 Unmarshaller由JAXBContext创建。 接下来,解组器将传递一个StringReader,其内容为刚刚创建的XML。 解组器返回一个对象,该对象被强制转换为联系人。 将针对新的Contact实例测试Contact的原始实例,以查看它们是否等效。 输出应显示:

Are the instances equivalent: true.
摘要

在此示例中,Contact的实例被转换为XML,并且在JAXB的帮助下,所得的XML被转换回Contact实例。 JAXB是一种将对象的状态映射到XML并将XML映射回对象的体系结构。

参考文献

http://www.techrepublic.com/blog/programming-and-development/jaxb-20-offers-improved-xml-binding-in-java/498

http://www.vogella.com/articles/JAXB/article.html

http://en.wikipedia.org/wiki/JAXB

翻译自: https://www.javacodegeeks.com/2014/10/the-jaxb-well-known-secret.html