jaxb解析字符串xml
尽管用于XML绑定的Java体系结构 ( JAXB )在名义情况下(尤其是自Java SE 6以来) 相当容易使用,但它也存在许多细微差别。 一些常见的细微差别是由于无法将 XML架构定义 (XSD)类型与Java 类型精确匹配 ( 绑定 )。 这篇文章看一个具体的例子,它还演示了当JAXB编译器生成Java类时,实施相同XML结构的不同XSD构造如何导致不同的Java类型。
下一个代码清单(用于Food.xsd
)定义了食物类型的架构。 XSD要求有效的XML将具有一个称为“食物”的根元素,并带有三个嵌套元素“蔬菜”,“水果”和“甜点”。 尽管用于指定“ Vegetable”和“ Dessert”元素的方法与用于指定“ Fruit”元素的方法不同,但是两种方法都导致相似的“有效XML”。 “ Vegetable”和“ Dessert”元素直接声明为稍后在XSD中定义的指定simpleType
的元素。 “水果”元素是通过引用( ref=
)定义到另一个包含simpleType
定义元素的。
Food.xsd
<?xml version="1.0" encoding="utf-8"?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:dustin="http://marxsoftware.blogspot.com/foodxml"
targetNamespace="http://marxsoftware.blogspot.com/foodxml"
elementFormDefault="qualified"
attributeFormDefault="unqualified">
<xs:element name="Food">
<xs:complexType>
<xs:sequence>
<xs:element name="Vegetable" type="dustin:Vegetable" />
<xs:element ref="dustin:Fruit" />
<xs:element name="Dessert" type="dustin:Dessert" />
</xs:sequence>
</xs:complexType>
</xs:element>
<!--
Direct simple type that restricts xs:string will become enum in
JAXB-generated Java class.
-->
<xs:simpleType name="Vegetable">
<xs:restriction base="xs:string">
<xs:enumeration value="Carrot"/>
<xs:enumeration value="Squash"/>
<xs:enumeration value="Spinach"/>
<xs:enumeration value="Celery"/>
</xs:restriction>
</xs:simpleType>
<!--
Simple type that restricts xs:string but is wrapped in xs:element
(making it an Element rather than a SimpleType) will become Java
String in JAXB-generated Java class for Elements that reference it.
-->
<xs:element name="Fruit">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:enumeration value="Watermelon"/>
<xs:enumeration value="Apple"/>
<xs:enumeration value="Orange"/>
<xs:enumeration value="Grape"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<!--
Direct simple type that restricts xs:string will become enum in
JAXB-generated Java class.
-->
<xs:simpleType name="Dessert">
<xs:restriction base="xs:string">
<xs:enumeration value="Pie"/>
<xs:enumeration value="Cake"/>
<xs:enumeration value="Ice Cream"/>
</xs:restriction>
</xs:simpleType>
</xs:schema>
尽管在模式中对Vegetable
元素和Dessert
元素的定义与对Fruit
定义不同,但是生成的有效XML是相同的。 接下来在food1.xml
的代码清单中显示一个有效的XML文件。
food1.xml
<?xml version="1.0" encoding="utf-8"?>
<Food xmlns="http://marxsoftware.blogspot.com/foodxml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Vegetable>Spinach</Vegetable>
<Fruit>Watermelon</Fruit>
<Dessert>Pie</Dessert>
</Food>
此时,我将使用一个简单的Groovy脚本针对上述XSD验证上述XML。 接下来显示此Groovy XML验证脚本的代码( validateXmlAgainstXsd.groovy
)。
validateXmlAgainstXsd.groovy
#!/usr/bin/env groovy
// validateXmlAgainstXsd.groovy
//
// Accepts paths/names of two files. The first is the XML file to be validated
// and the second is the XSD against which to validate that XML.
if (args.length < 2)
{
println "USAGE: groovy validateXmlAgainstXsd.groovy <xmlFile> <xsdFile>"
System.exit(-1)
}
String xml = args[0]
String xsd = args[1]
import javax.xml.validation.Schema
import javax.xml.validation.SchemaFactory
import javax.xml.validation.Validator
try
{
SchemaFactory schemaFactory =
SchemaFactory.newInstance(javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI)
Schema schema = schemaFactory.newSchema(new File(xsd))
Validator validator = schema.newValidator()
validator.validate(new javax.xml.transform.stream.StreamSource(xml))
}
catch (Exception exception)
{
println "\nERROR: Unable to validate ${xml} against ${xsd} due to '${exception}'\n"
System.exit(-1)
}
println "\nXML file ${xml} validated successfully against ${xsd}.\n"
下一个屏幕快照展示了针对food1.xml
和Food.xsd
运行上述Groovy XML验证脚本。
到目前为止,本文的目的是展示XSD中的不同方法如何导致相同的XML有效。 尽管这些不同的XSD方法规定了相同的有效XML,但是当使用JAXB生成基于XSD的类时,它们会导致不同的Java类行为。 下一个屏幕快照演示了针对Food.xsd
运行JDK提供的JAXB xjc编译器以生成Java类。
上面显示的JAXB生成的输出表明Java类是为“ Vegetable”和“ Dessert”元素创建的,而不是为“ Fruit”元素创建的。 这是因为在XSD中,“蔬菜”和“甜点”的定义不同于“水果”。 下一个代码清单是由xjc
编译器生成的Food.java
类的。 由此可见,生成的Food.java
类引用了针对Vegetable
和Dessert
特定生成的Java类型,但仅引用了Fruit
的通用Java字符串。
Food.java(由JAXB jxc编译器生成)
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2015.02.11 at 10:17:32 PM MST
//
package com.blogspot.marxsoftware.foodxml;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
import javax.xml.bind.annotation.XmlSchemaType;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for anonymous complex type.
*
* <p>The following schema fragment specifies the expected content contained within this class.
*
* <pre>
* <complexType>
* <complexContent>
* <restriction base="{http://www.w3.org/2001/XMLSchema}anyType">
* <sequence>
* <element name="Vegetable" type="{http://marxsoftware.blogspot.com/foodxml}Vegetable"/>
* <element ref="{http://marxsoftware.blogspot.com/foodxml}Fruit"/>
* <element name="Dessert" type="{http://marxsoftware.blogspot.com/foodxml}Dessert"/>
* </sequence>
* </restriction>
* </complexContent>
* </complexType>
* </pre>
*
*
*/
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
"vegetable",
"fruit",
"dessert"
})
@XmlRootElement(name = "Food")
public class Food {
@XmlElement(name = "Vegetable", required = true)
@XmlSchemaType(name = "string")
protected Vegetable vegetable;
@XmlElement(name = "Fruit", required = true)
protected String fruit;
@XmlElement(name = "Dessert", required = true)
@XmlSchemaType(name = "string")
protected Dessert dessert;
/**
* Gets the value of the vegetable property.
*
* @return
* possible object is
* {@link Vegetable }
*
*/
public Vegetable getVegetable() {
return vegetable;
}
/**
* Sets the value of the vegetable property.
*
* @param value
* allowed object is
* {@link Vegetable }
*
*/
public void setVegetable(Vegetable value) {
this.vegetable = value;
}
/**
* Gets the value of the fruit property.
*
* @return
* possible object is
* {@link String }
*
*/
public String getFruit() {
return fruit;
}
/**
* Sets the value of the fruit property.
*
* @param value
* allowed object is
* {@link String }
*
*/
public void setFruit(String value) {
this.fruit = value;
}
/**
* Gets the value of the dessert property.
*
* @return
* possible object is
* {@link Dessert }
*
*/
public Dessert getDessert() {
return dessert;
}
/**
* Sets the value of the dessert property.
*
* @param value
* allowed object is
* {@link Dessert }
*
*/
public void setDessert(Dessert value) {
this.dessert = value;
}
}
具有特定的Vegetable
和Dessert
类的优点是,与一般的Java String
相比,它们具有附加的类型安全性。 Vegetable.java
和Dessert.java
实际上都是枚举,因为它们来自XSD中的枚举值。 接下来的两个代码清单中显示了这两个生成的枚举。
Vegetable.java(使用JAXB xjc编译器生成)
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2015.02.11 at 10:17:32 PM MST
//
package com.blogspot.marxsoftware.foodxml;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlEnumValue;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for Vegetable.
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
* <pre>
* <simpleType name="Vegetable">
* <restriction base="{http://www.w3.org/2001/XMLSchema}string">
* <enumeration value="Carrot"/>
* <enumeration value="Squash"/>
* <enumeration value="Spinach"/>
* <enumeration value="Celery"/>
* </restriction>
* </simpleType>
* </pre>
*
*/
@XmlType(name = "Vegetable")
@XmlEnum
public enum Vegetable {
@XmlEnumValue("Carrot")
CARROT("Carrot"),
@XmlEnumValue("Squash")
SQUASH("Squash"),
@XmlEnumValue("Spinach")
SPINACH("Spinach"),
@XmlEnumValue("Celery")
CELERY("Celery");
private final String value;
Vegetable(String v) {
value = v;
}
public String value() {
return value;
}
public static Vegetable fromValue(String v) {
for (Vegetable c: Vegetable.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
}
Dessert.java(使用JAXB xjc编译器生成)
//
// This file was generated by the JavaTM Architecture for XML Binding(JAXB) Reference Implementation, v2.2.8-b130911.1802
// See <a href="http://java.sun.com/xml/jaxb">http://java.sun.com/xml/jaxb</a>
// Any modifications to this file will be lost upon recompilation of the source schema.
// Generated on: 2015.02.11 at 10:17:32 PM MST
//
package com.blogspot.marxsoftware.foodxml;
import javax.xml.bind.annotation.XmlEnum;
import javax.xml.bind.annotation.XmlEnumValue;
import javax.xml.bind.annotation.XmlType;
/**
* <p>Java class for Dessert.
*
* <p>The following schema fragment specifies the expected content contained within this class.
* <p>
* <pre>
* <simpleType name="Dessert">
* <restriction base="{http://www.w3.org/2001/XMLSchema}string">
* <enumeration value="Pie"/>
* <enumeration value="Cake"/>
* <enumeration value="Ice Cream"/>
* </restriction>
* </simpleType>
* </pre>
*
*/
@XmlType(name = "Dessert")
@XmlEnum
public enum Dessert {
@XmlEnumValue("Pie")
PIE("Pie"),
@XmlEnumValue("Cake")
CAKE("Cake"),
@XmlEnumValue("Ice Cream")
ICE_CREAM("Ice Cream");
private final String value;
Dessert(String v) {
value = v;
}
public String value() {
return value;
}
public static Dessert fromValue(String v) {
for (Dessert c: Dessert.values()) {
if (c.value.equals(v)) {
return c;
}
}
throw new IllegalArgumentException(v);
}
}
为XML元素生成枚举可确保只能用Java表示这些元素的有效值。
结论
JAXB使将Java映射到XML相对容易,但是由于Java和XML类型之间没有一对一的映射,因此在某些情况下,为特定XSD规定元素生成的Java类型并不明显。 这篇文章显示了两种不同的构建XSD来强制使用相同的基本XML结构的方法如何导致用JAXB xjc
编译器生成的Java类产生截然不同的结果。 在本文中显示的示例中,直接声明XSD上的元素在simpleType
将XSD的string
限制为一组特定的枚举值比将元素声明为引用其他封装了simpleType
的限制字符串枚举值的元素更可取,因为类型安全这是在生成枚举而不是使用常规Java String
的。
jaxb解析字符串xml