用来记录一些原创性的总结
从面向对象的角度来讲,Java类的定义与XML的Schema文件的定义是一致的,都是用来定义具体对象的结构的。而具体的Java对象则将跟XML文件相对应,XML的具体内容则是由对应的Schema文件约束的。通过Java对象可以转换为XML,也可以通过对应的Java类定义转换为XML对应的Schema文件。同样的,我们可以通过XML转换为对应的Java对象,也可以通过XML对应的Schema文件转换对应的Java类定义。
schemegen是JDK自带的一个工具,用来基于java或class文件生成对应的schema文件。对应的类可以通过JAXB注解来定义XML映射关系。我们先来看一个简单的示例,假设有如下这些java类需要用来生成schema文件。
@XmlAccessorType(XmlAccessType.FIELD)
@XmlRootElement(name = "root")
public class RootObject {
private String prop1;
private String prop2;
@XmlElementRef
private SuperClass superClass;
public String getProp1() {
return prop1;
}
public void setProp1(String prop1) {
this.prop1 = prop1;
}
public String getProp2() {
return prop2;
}
public void setProp2(String prop2) {
this.prop2 = prop2;
}
public SuperClass getSuperClass() {
return superClass;
}
public void setSuperClass(SuperClass superClass) {
this.superClass = superClass;
}
}
public class SuperClass {
private String superProp1;
private String superProp2;
public String getSuperProp1() {
return superProp1;
}
public void setSuperProp1(String superProp1) {
this.superProp1 = superProp1;
}
public String getSuperProp2() {
return superProp2;
}
public void setSuperProp2(String superProp2) {
this.superProp2 = superProp2;
}
}
@XmlRootElement(name="sub1")
public class SubClass1 extends SuperClass {
private String subProp1;
private String subProp2;
public String getSubProp1() {
return subProp1;
}
public void setSubProp1(String subProp1) {
this.subProp1 = subProp1;
}
public String getSubProp2() {
return subProp2;
}
public void setSubProp2(String subProp2) {
this.subProp2 = subProp2;
}
}
@XmlRootElement(name="sub2")
public class SubClass2 extends SuperClass {
private String subProp1;
private String subProp2;
public String getSubProp1() {
return subProp1;
}
public void setSubProp1(String subProp1) {
this.subProp1 = subProp1;
}
public String getSubProp2() {
return subProp2;
}
public void setSubProp2(String subProp2) {
this.subProp2 = subProp2;
}
}
这些类都是放在com.elim.jaxb包下面的,所以在命令行窗口定位到源文件根路径,运行命令即可在当前目录生成一个schema1.xsd文件。因为我们在源文件里面没有指定schema,所以它们都将使用相同的默认schema,这时候生成的schema文件只有一个。如果我们在源文件中指定了多个不同的schema时,生成出来的也将有多个schema文件。
schemagen com/elim/jaxb/*.java
生成出来的schema文件内容如下:
<?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="root" type="rootObject"/>
<xs:element name="sub1" type="subClass1"/>
<xs:element name="sub2" type="subClass2"/>
<xs:complexType name="rootObject">
<xs:sequence>
<xs:element name="prop1" type="xs:string" minOccurs="0"/>
<xs:element name="prop2" type="xs:string" minOccurs="0"/>
<xs:choice>
<xs:element ref="sub1"/>
<xs:element ref="sub2"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="superClass">
<xs:sequence>
<xs:element name="superProp1" type="xs:string" minOccurs="0"/>
<xs:element name="superProp2" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="subClass1">
<xs:complexContent>
<xs:extension base="superClass">
<xs:sequence>
<xs:element name="subProp1" type="xs:string" minOccurs="0"/>
<xs:element name="subProp2" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="subClass2">
<xs:complexContent>
<xs:extension base="superClass">
<xs:sequence>
<xs:element name="subProp1" type="xs:string" minOccurs="0"/>
<xs:element name="subProp2" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
如果需要指定namespace,可以在具体的类上通过@XmlType或@XmlRootElement的namespace指定,如:
@XmlRootElement(name="sub1", namespace="http://elim.com/jaxb")
public class SubClass1 extends SuperClass {
private String subProp1;
private String subProp2;
public String getSubProp1() {
return subProp1;
}
public void setSubProp1(String subProp1) {
this.subProp1 = subProp1;
}
public String getSubProp2() {
return subProp2;
}
public void setSubProp2(String subProp2) {
this.subProp2 = subProp2;
}
}
如果整个包里面的类对应的schema都属于同一个namespace,则可以建立对应的package-info.java文件,然后通过@XmlSchema指定schema相关的信息,包括namespace,如:
@javax.xml.bind.annotation.XmlSchema(namespace="http://elim.com/jaxb",
elementFormDefault=XmlNsForm.UNQUALIFIED,
attributeFormDefault=XmlNsForm.UNQUALIFIED)
package com.elim.jaxb;
import javax.xml.bind.annotation.XmlNsForm;
使用了上面的配置后生成的schema文件内容如下:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="unqualified" version="1.0" targetNamespace="http://elim.com/jaxb" xmlns:tns="http://elim.com/jaxb" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="root" type="tns:rootObject"/>
<xs:element name="sub1" type="tns:subClass1"/>
<xs:element name="sub2" type="tns:subClass2"/>
<xs:complexType name="rootObject">
<xs:sequence>
<xs:element name="prop1" type="xs:string" minOccurs="0"/>
<xs:element name="prop2" type="xs:string" minOccurs="0"/>
<xs:choice>
<xs:element ref="tns:sub1"/>
<xs:element ref="tns:sub2"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="superClass">
<xs:sequence>
<xs:element name="superProp1" type="xs:string" minOccurs="0"/>
<xs:element name="superProp2" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="subClass1">
<xs:complexContent>
<xs:extension base="tns:superClass">
<xs:sequence>
<xs:element name="subProp1" type="xs:string" minOccurs="0"/>
<xs:element name="subProp2" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="subClass2">
<xs:complexContent>
<xs:extension base="tns:superClass">
<xs:sequence>
<xs:element name="subProp1" type="xs:string" minOccurs="0"/>
<xs:element name="subProp2" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
上面的示例也可以单独指定需要生成schema的java类:
schemagen com/elim/jaxb/RootObject.java com/elim/jaxb/SuperClass.java com/elim/jaxb/SubClass1.java com/elim/jaxb/SubClass2.java
除了基于Java源文件生成schema外,也可以基于编译好的class文件生成schema文件,只需要把后面的java文件替换为class即可,但对于class文件我们只需要确保它们在classpath下,并且指定对应的class名称即可,所以对于上述的示例,如果改为使用class文件生成schema,我们只需要定位到class文件根目录,运行如下指令即可。不需要单独指定SuperClass,因为它已经可以被RootObject关联到了。
schemagen com.elim.jaxb.RootObject com.elim.jaxb.SubClass1 com.elim.jaxb.SubClass2
不管是基于java文件还是基于class文件生成schema,都需要确保它们中应用的class都是在类路径下的。
以上只是一个简单的示例,接下来我们来看以下schemagen的语法,其语法如下:
schemagen [options] javafiles
其中的options支持以下选项,为保持原文语义,直接摘自官方文档:
基于Schema文件生成对应的Java代码,可以通过JDK自带的xjc工具来生成。xjc的语法是:
xjc [options] schemaFile [-b bindinfo]
以下参数信息的说明摘自官方网站。
可选的options如下:
更详细的信息请参考官方文档。
以下是一份schema文档,我们可以通过xjc -p com.elim.jaxb schema.xsd
来指定生成的类都放在com.elim.jaxb
包中。
<?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="root" type="rootObject"/>
<xs:element name="sub1" type="subClass1"/>
<xs:element name="sub2" type="subClass2"/>
<xs:complexType name="rootObject">
<xs:sequence>
<xs:element name="prop1" type="xs:string" minOccurs="0"/>
<xs:element name="prop2" type="xs:string" minOccurs="0"/>
<xs:choice>
<xs:element ref="sub1"/>
<xs:element ref="sub2"/>
</xs:choice>
</xs:sequence>
</xs:complexType>
<xs:complexType name="superClass">
<xs:sequence>
<xs:element name="superProp1" type="xs:string" minOccurs="0"/>
<xs:element name="superProp2" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="subClass1">
<xs:complexContent>
<xs:extension base="superClass">
<xs:sequence>
<xs:element name="subProp1" type="xs:string" minOccurs="0"/>
<xs:element name="subProp2" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:complexType name="subClass2">
<xs:complexContent>
<xs:extension base="superClass">
<xs:sequence>
<xs:element name="subProp1" type="xs:string" minOccurs="0"/>
<xs:element name="subProp2" type="xs:string" minOccurs="0"/>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>
生成的java类如下:
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "rootObject", propOrder = {
"prop1",
"prop2",
"sub1",
"sub2"
})
public class RootObject {
protected String prop1;
protected String prop2;
@XmlElement(namespace = "http://elim.com/jaxb")
protected SubClass1 sub1;
@XmlElement(namespace = "http://elim.com/jaxb")
protected SubClass2 sub2;
public String getProp1() {
return prop1;
}
public void setProp1(String value) {
this.prop1 = value;
}
public String getProp2() {
return prop2;
}
public void setProp2(String value) {
this.prop2 = value;
}
public SubClass1 getSub1() {
return sub1;
}
public void setSub1(SubClass1 value) {
this.sub1 = value;
}
public SubClass2 getSub2() {
return sub2;
}
public void setSub2(SubClass2 value) {
this.sub2 = value;
}
}
通过Schema生成的RootObject类跟我们原本定义的RootObject类还是有一点出入的,这可以根据我们的实际需要在生成的类的基础上再加以调整。
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "superClass", propOrder = {
"superProp1",
"superProp2"
})
@XmlSeeAlso({
SubClass2 .class,
SubClass1 .class
})
public class SuperClass {
protected String superProp1;
protected String superProp2;
public String getSuperProp1() {
return superProp1;
}
public void setSuperProp1(String value) {
this.superProp1 = value;
}
public String getSuperProp2() {
return superProp2;
}
public void setSuperProp2(String value) {
this.superProp2 = value;
}
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "subClass1", propOrder = {
"subProp1",
"subProp2"
})
public class SubClass1
extends SuperClass
{
protected String subProp1;
protected String subProp2;
public String getSubProp1() {
return subProp1;
}
public void setSubProp1(String value) {
this.subProp1 = value;
}
public String getSubProp2() {
return subProp2;
}
public void setSubProp2(String value) {
this.subProp2 = value;
}
}
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "subClass2", propOrder = {
"subProp1",
"subProp2"
})
public class SubClass2
extends SuperClass
{
protected String subProp1;
protected String subProp2;
public String getSubProp1() {
return subProp1;
}
public void setSubProp1(String value) {
this.subProp1 = value;
}
public String getSubProp2() {
return subProp2;
}
public void setSubProp2(String value) {
this.subProp2 = value;
}
}
下面是生成的package-info.java的内容。
@javax.xml.bind.annotation.XmlSchema(namespace = "http://elim.com/jaxb")
package com.elim.jaxb;
@XmlRegistry
public class ObjectFactory {
private final static QName _Sub2_QNAME = new QName("http://elim.com/jaxb", "sub2");
private final static QName _Root_QNAME = new QName("http://elim.com/jaxb", "root");
private final static QName _Sub1_QNAME = new QName("http://elim.com/jaxb", "sub1");
public ObjectFactory() {
}
public SubClass2 createSubClass2() {
return new SubClass2();
}
public RootObject createRootObject() {
return new RootObject();
}
public SubClass1 createSubClass1() {
return new SubClass1();
}
public SuperClass createSuperClass() {
return new SuperClass();
}
@XmlElementDecl(namespace = "http://elim.com/jaxb", name = "sub2")
public JAXBElement<SubClass2> createSub2(SubClass2 value) {
return new JAXBElement<SubClass2>(_Sub2_QNAME, SubClass2 .class, null, value);
}
@XmlElementDecl(namespace = "http://elim.com/jaxb", name = "root")
public JAXBElement<RootObject> createRoot(RootObject value) {
return new JAXBElement<RootObject>(_Root_QNAME, RootObject.class, null, value);
}
@XmlElementDecl(namespace = "http://elim.com/jaxb", name = "sub1")
public JAXBElement<SubClass1> createSub1(SubClass1 value) {
return new JAXBElement<SubClass1>(_Sub1_QNAME, SubClass1 .class, null, value);
}
}
无论是通过Java生成Schema,还是通过Schema生成Java,生成出来的跟预期的可能有点小出入,但它们都是正确的,在保证正确的基础上,我们可以对生成出来的内容进行一些细微的调整。
Schema文件是XML内容的定义,在进行marshal和unmarshal时我们可以通过指定对应的Schema文件进行Schema校验,这样当对应的XML不满足Schema的规定时就会转换失败。下面的示例就是在unmarshal的时候指定了需要用来做校验的Schema文件,SchemaFactory不是线程安全的,但是Schema是线程安全的。在marshal的过程中也可以通过Marshaller指定对应的Schema。
@Test
public void testUnmarshal() throws Exception {
JAXBContext jaxbContext = JAXBContext.newInstance(RootObject.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
SchemaFactory schemaFactory = SchemaFactory.newInstance(XMLConstants.W3C_XML_SCHEMA_NS_URI);
URL resource = this.getClass().getResource("/schema.xsd");
Schema schema = schemaFactory.newSchema(resource);
//指定需要用来做校验的Schema
unmarshaller.setSchema(schema);
URL xml = this.getClass().getResource("/test.xml");
unmarshaller.unmarshal(xml);
}
(完)