Статьи

Создайте ваши классы JAXB в секунду с xjc

Поскольку JAXB является частью JDK, это одна из наиболее часто используемых платформ для обработки документов XML. Он предоставляет удобный способ извлечения и хранения данных из документов XML в классы Java. Поскольку почти каждый разработчик Java уже использовал JAXB, я не буду объяснять различные аннотации JAXB. Вместо этого я сосредоточусь на небольшом инструменте командной строки под названием xjc и покажу, как генерировать классы привязки на основе существующего описания схемы XSD.

Реализация всех классов привязки для существующего интерфейса XML может быть трудоемкой и утомительной задачей. Но хорошая новость в том, что вам не нужно это делать. Если у вас есть описание схемы XSD, вы можете использовать компилятор связывания xjc для создания необходимых классов. И даже лучше, xjc является частью JDK. Поэтому нет необходимости во внешних инструментах, и вы всегда должны иметь их под рукой, если это необходимо.

Использование xjc

Как видно из фрагмента ниже, xjc поддерживает множество опций. Наиболее важными являются:

  • -d определить, где сгенерированные классы должны храниться в файловой системе ,
  • -p определить пакет для использования и, конечно,
  • -Помогите, если вам нужно что-нибудь еще.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
Usage: xjc [-options ...] <schema file/URL/dir/jar> ... [-b <bindinfo>] ...
If dir is specified, all schema files in it will be compiled.
If jar is specified, /META-INF/sun-jaxb.episode binding file will be compiled.
Options:
  -nv                :  do not perform strict validation of the input schema(s)
  -extension         :  allow vendor extensions - do not strictly follow the
                        Compatibility Rules and App E.2 from the JAXB Spec
  -b <file/dir>      :  specify external bindings files (each <file> must have its own -b)
                        If a directory is given, **/*.xjb is searched
  -d <dir>           :  generated files will go into this directory
  -p <pkg>           :  specifies the target package
  -httpproxy <proxy> :  set HTTP/HTTPS proxy. Format is [user[:password]@]proxyHost:proxyPort
  -httpproxyfile <f> :  Works like -httpproxy but takes the argument in a file to protect password
  -classpath <arg>   :  specify where to find user class files
  -catalog <file>    :  specify catalog files to resolve external entity references
                        support TR9401, XCatalog, and OASIS XML Catalog format.
  -readOnly          :  generated files will be in read-only mode
  -npa               :  suppress generation of package level annotations (**/package-info.java)
  -no-header         :  suppress generation of a file header with timestamp
  -target (2.0|2.1)  :  behave like XJC 2.0 or 2.1 and generate code that doesnt use any 2.2 features.
  -encoding <encoding> :  specify character encoding for generated source files
  -enableIntrospection :  enable correct generation of Boolean getters/setters to enable Bean Introspection apis
  -contentForWildcard  :  generates content property for types with multiple xs:any derived elements
  -xmlschema         :  treat input as W3C XML Schema (default)
  -relaxng           :  treat input as RELAX NG (experimental,unsupported)
  -relaxng-compact   :  treat input as RELAX NG compact syntax (experimental,unsupported)
  -dtd               :  treat input as XML DTD (experimental,unsupported)
  -wsdl              :  treat input as WSDL and compile schemas inside it (experimental,unsupported)
  -verbose           :  be extra verbose
  -quiet             :  suppress compiler output
  -help              :  display this help message
  -version           :  display version information
  -fullversion       :  display full version information
 
 
Extensions:
  -Xinject-code      :  inject specified Java code fragments into the generated code
  -Xlocator          :  enable source location support for generated code
  -Xsync-methods     :  generate accessor methods with the 'synchronized' keyword
  -mark-generated    :  mark the generated code as @javax.annotation.Generated
  -episode <FILE>    :  generate the episode file for separate compilation

пример

Хорошо, давайте посмотрим на пример. Мы будем использовать следующее определение схемы XSD и xjc для генерации классов Author и Book с описанными свойствами и необходимыми аннотациями JAXB.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema version="1.0" xmlns:xs="http://www.w3.org/2001/XMLSchema">
 
  <xs:element name="author" type="author"/>
 
  <xs:element name="book" type="book"/>
 
  <xs:complexType name="author">
    <xs:sequence>
      <xs:element name="firstName" type="xs:string" minOccurs="0"/>
      <xs:element name="lastName" type="xs:string" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>
 
  <xs:complexType name="book">
    <xs:sequence>
      <xs:element ref="author" minOccurs="0"/>
      <xs:element name="pages" type="xs:int"/>
      <xs:element name="publicationDate" type="xs:dateTime" minOccurs="0"/>
      <xs:element name="title" type="xs:string" minOccurs="0"/>
    </xs:sequence>
  </xs:complexType>
</xs:schema>

Следующая команда вызывает xjc и предоставляет целевой каталог для сгенерированных классов, пакета и файла схемы XSD.

1
2
3
4
5
6
7
xjc -d src -p blog.thoughts.on.java schema.xsd
 
parsing a schema...
compiling a schema...
blog\thoughts\on\java\Author.java
blog\thoughts\on\java\Book.java
blog\thoughts\on\java\ObjectFactory.java

ОК, операция успешно завершена, и теперь у нас есть 3 сгенерированных класса в нашем каталоге src. Это может быть на один больше, чем некоторые ожидали. Итак, давайте посмотрим на каждого из них.

Классы Author и Book выглядят как ожидалось. Они содержат свойства, описанные в схеме XSD, и необходимые аннотации JAXB.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2014.01.13 at 07:38:24 PM CET
//
 
 
package blog.thoughts.on.java;
 
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlType;
 
 
/**
 * <p>Java class for author complex type.
 *
 * <p>The following schema fragment specifies the expected content contained within this class.
 *
 * <pre>
 * <complexType name="author">
 *   <complexContent>
 *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       <sequence>
 *         <element name="firstName" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
 *         <element name="lastName" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
 *       </sequence>
 *     </restriction>
 *   </complexContent>
 * </complexType>
 * </pre>
 *
 *
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "author", propOrder = {
    "firstName",
    "lastName"
})
public class Author {
 
    protected String firstName;
    protected String lastName;
 
    /**
     * Gets the value of the firstName property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *    
     */
    public String getFirstName() {
        return firstName;
    }
 
    /**
     * Sets the value of the firstName property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *    
     */
    public void setFirstName(String value) {
        this.firstName = value;
    }
 
    /**
     * Gets the value of the lastName property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *    
     */
    public String getLastName() {
        return lastName;
    }
 
    /**
     * Sets the value of the lastName property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *    
     */
    public void setLastName(String value) {
        this.lastName = value;
    }
 
}
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2014.01.13 at 07:38:24 PM CET
//
 
 
package blog.thoughts.on.java;
 
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
import javax.xml.datatype.XMLGregorianCalendar;
 
 
/**
 * <p>Java class for book complex type.
 *
 * <p>The following schema fragment specifies the expected content contained within this class.
 *
 * <pre>
 * <complexType name="book">
 *   <complexContent>
 *     <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
 *       <sequence>
 *         <element ref="{}author" minOccurs="0"/>
 *         <element name="pages" type="{http://www.w3.org/2001/XMLSchema}int"/>
 *         <element name="publicationDate" type="{http://www.w3.org/2001/XMLSchema}dateTime" minOccurs="0"/>
 *         <element name="title" type="{http://www.w3.org/2001/XMLSchema}string" minOccurs="0"/>
 *       </sequence>
 *     </restriction>
 *   </complexContent>
 * </complexType>
 * </pre>
 *
 *
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "book", propOrder = {
    "author",
    "pages",
    "publicationDate",
    "title"
})
public class Book {
 
    protected Author author;
    protected int pages;
    @XmlSchemaType(name = "dateTime")
    protected XMLGregorianCalendar publicationDate;
    protected String title;
 
    /**
     * Gets the value of the author property.
     *
     * @return
     *     possible object is
     *     {@link Author }
     *    
     */
    public Author getAuthor() {
        return author;
    }
 
    /**
     * Sets the value of the author property.
     *
     * @param value
     *     allowed object is
     *     {@link Author }
     *    
     */
    public void setAuthor(Author value) {
        this.author = value;
    }
 
    /**
     * Gets the value of the pages property.
     *
     */
    public int getPages() {
        return pages;
    }
 
    /**
     * Sets the value of the pages property.
     *
     */
    public void setPages(int value) {
        this.pages = value;
    }
 
    /**
     * Gets the value of the publicationDate property.
     *
     * @return
     *     possible object is
     *     {@link XMLGregorianCalendar }
     *    
     */
    public XMLGregorianCalendar getPublicationDate() {
        return publicationDate;
    }
 
    /**
     * Sets the value of the publicationDate property.
     *
     * @param value
     *     allowed object is
     *     {@link XMLGregorianCalendar }
     *    
     */
    public void setPublicationDate(XMLGregorianCalendar value) {
        this.publicationDate = value;
    }
 
    /**
     * Gets the value of the title property.
     *
     * @return
     *     possible object is
     *     {@link String }
     *    
     */
    public String getTitle() {
        return title;
    }
 
    /**
     * Sets the value of the title property.
     *
     * @param value
     *     allowed object is
     *     {@link String }
     *    
     */
    public void setTitle(String value) {
        this.title = value;
    }
 
}

Третий и, возможно, неожиданный класс — это класс ObjectFactory. Он содержит фабричные методы для каждого сгенерированного класса или интерфейса. Это может быть очень полезно, если вам нужно создать JAXBElement представления ваших объектов.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.4-2
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2014.01.13 at 07:38:24 PM CET
//
 
 
package blog.thoughts.on.java;
 
import javax.xml.bind.JAXBElement;
import javax.xml.bind.annotation.XmlElementDecl;
import javax.xml.bind.annotation.XmlRegistry;
import javax.xml.namespace.QName;
 
 
/**
 * This object contains factory methods for each
 * Java content interface and Java element interface
 * generated in the blog.thoughts.on.java package.
 * <p>An ObjectFactory allows you to programatically
 * construct new instances of the Java representation
 * for XML content. The Java representation of XML
 * content can consist of schema derived interfaces
 * and classes representing the binding of schema
 * type definitions, element declarations and model
 * groups.  Factory methods for each of these are
 * provided in this class.
 *
 */
@XmlRegistry
public class ObjectFactory {
 
    private final static QName _Author_QNAME = new QName("", "author");
    private final static QName _Book_QNAME = new QName("", "book");
 
    /**
     * Create a new ObjectFactory that can be used to create new instances of schema derived classes for package: blog.thoughts.on.java
     *
     */
    public ObjectFactory() {
    }
 
    /**
     * Create an instance of {@link Author }
     *
     */
    public Author createAuthor() {
        return new Author();
    }
 
    /**
     * Create an instance of {@link Book }
     *
     */
    public Book createBook() {
        return new Book();
    }
 
    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link Author }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "", name = "author")
    public JAXBElement<Author> createAuthor(Author value) {
        return new JAXBElement<Author>(_Author_QNAME, Author.class, null, value);
    }
 
    /**
     * Create an instance of {@link JAXBElement }{@code <}{@link Book }{@code >}}
     *
     */
    @XmlElementDecl(namespace = "", name = "book")
    public JAXBElement<Book> createBook(Book value) {
        return new JAXBElement<Book>(_Book_QNAME, Book.class, null, value);
    }
 
}

Вывод

Мы взглянули на xjc и использовали его для генерации обязательных классов привязки для существующего определения схемы XSD. xjc сгенерировал класс для каждого сложного типа и дополнительный фабричный класс, чтобы упростить создание представлений JAXBElement.

Что вы думаете о xjc и сгенерированном коде? Пожалуйста, оставьте мне комментарий и расскажите мне об этом.

Я думаю, что этот инструмент генерирует очень чистый код и экономит много времени. В большинстве случаев сгенерированный код может быть непосредственно добавлен в проект. Но даже если это не так, гораздо быстрее выполнить рефакторинг на основе сгенерированного кода, чем делать все самому.