JAXB解析xml转换成类的实现方式
作者:码上走人
项目需要将一些公共配置项写成xml文件,启动时转化成对应的类。
查找了一些资料,发现JAXB完全可以满足自己的功能需求。
pom依赖
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version>
</dependency>主要注解
@XmlRootElement
@XmlRootElement定义xml根节点的信息。主要功能如下:
1. 将元素与XML模式类型相关联
@XmlRootElement
class Point {
int x;
int y;
Point(int _x,int _y) {x=_x;y=_y;}
}
//Example: Code fragment corresponding to XML output
marshal( new Point(3,5), System.out);
<!-- Example: XML output -->
<point>
<x> 3 </x>
<y> 5 </y>
</point>
//注释在模式中生成全局元素声明。全局元素声明与类映射到的XML模式类型相关联。
<!-- Example: XML schema definition -->
<xs:element name="point" type="point"/>
<xs:complexType name="point">
<xs:sequence>
<xs:element name="x" type="xs:int"/>
<xs:element name="y" type="xs:int"/>
</xs:sequence>
</xs:complexType>2. 将全局元素与类所属的XML模式类型相关联
@XmlRootElement(name="PriceElement")
public class USPrice {
@XmlElement
public java.math.BigDecimal price;
}
Example: XML schema definition -->
<xs:element name="PriceElement" type="USPrice"/>
<xs:complexType name="USPrice">
<xs:sequence>
<xs:element name="price" type="xs:decimal"/>
</sequence>
</xs:complexType>@XmlElement
@XmlElement可以将xml元素转为对应的实体属性值。可转化普通属性、数组、对象
1. 将公共非静态非最终字段映射到本地元素
public class USPrice {
@XmlElement(name="itemprice")
public java.math.BigDecimal price;
}
<!-- Example: Local XML Schema element -->
<xs:complexType name="USPrice"/>
<xs:sequence>
<xs:element name="itemprice" type="xs:decimal" minOccurs="0"/>
</sequence>
</xs:complexType>2. 映射到可nullable元素。
public class USPrice {
@XmlElement(nillable=true)
public java.math.BigDecimal price;
}
<!-- Example: Local XML Schema element -->
<xs:complexType name="USPrice">
<xs:sequence>
<xs:element name="price" type="xs:decimal" nillable="true" minOccurs="0"/>
</sequence>
</xs:complexType>3. 映射到可nullable元素且必填的元素。
public class USPrice {
@XmlElement(nillable=true, required=true)
public java.math.BigDecimal price;
}
<!-- Example: Local XML Schema element -->
<xs:complexType name="USPrice">
<xs:sequence>
<xs:element name="price" type="xs:decimal" nillable="true" minOccurs="1"/>
</sequence>
</xs:complexType>@XmlElements
@XmlElements可以将不同的xml节点转换为实体数组对象。
public class Foo {
XmlElements(
XmlElement(name="A", type=Integer.class),
XmlElement(name="B", type=Float.class)
)
public List items;
}
// items = [1,2.5]时,得到的xml为
<A> 1 </A>
<B> 2.5 </B>
...
<!-- XML Schema fragment -->
<xs:complexType name="Foo">
<xs:sequence>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="A" type="xs:int"/>
<xs:element name="B" type="xs:float"/>
<xs:choice>
</xs:sequence>
</xs:complexType>映射到用另一个元素包裹的元素列表
// Mapped code fragment
public class Foo {
XmlElementWrapper(name="bar")
XmlElements(
XmlElement(name="A", type=Integer.class),
XmlElement(name="B", type=Float.class)
}
public List items;
}映射结果
<!-- XML Schema fragment -->
<xs:complexType name="Foo">
<xs:sequence>
<xs:element name="bar">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="A" type="xs:int"/>
<xs:element name="B" type="xs:float"/>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>使用适配器根据类型更改元素名称
class Foo {
XmlJavaTypeAdapter(QtoPAdapter.class)
XmlElements({
XmlElement(name="A",type=PX.class),
XmlElement(name="B",type=PY.class)
})
Q bar;
}
XmlType abstract class P {...}
XmlType(name="PX") class PX extends P {...}
XmlType(name="PY") class PY extends P {...}
<!-- XML Schema fragment -->
<xs:complexType name="Foo">
<xs:sequence>
<xs:element name="bar">
<xs:complexType>
<xs:choice minOccurs="0" maxOccurs="unbounded">
<xs:element name="A" type="PX"/>
<xs:element name="B" type="PY"/>
</xs:choice>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>@XmlElementWrapper
围绕XML表示生成包装器元素。
这主要是为了在集合周围生成一个包装器XML元素。
通过这个 @XmlElementWrapper注解可以减少包装类的创建。
//Example: code fragment
int[] names;
// XML Serialization Form 1 (Unwrapped collection)
<names> ... </names>
<names> ... </names>
// XML Serialization Form 2 ( Wrapped collection )
<wrapperElement>
<names> value-of-item </names>
<names> value-of-item </names>
....
</wrapperElement>@XmlElementWrapper 需要配合 @XmlElement 一起使用
@XmlList
@XmlList注释允许在单个元素中将多个值表示为空格分隔的标记
@XmlRootElement
class Foo {
@XmlElement
@XmlList
List<String> data;
}
the above code will produce XML like this:
<foo>
<data>abc def</data>
</foo>@XmlAttribute
@XmlAttribute可以获取到xml节点的属性字段值
@XmlElementRef
该注解所在属性是某些子类的父类,起到引用的作用。
同时标注得有@XmlElementRef的类属性,其子类上需要使用@XmlRootElement标注,否则转换异常,提示找不到具体的引用实现。
另外,转换时,需要将其子类的class一起传递到JAXBContext上下文中,否则也无法转换。
@XmlTransient
通常与 @XmlElement 须搭配使用的。
@XmlElement用在属性上,用于指定生成xml的节点名,@XmlTransient用在对应的getter方法上,起到关联的作用。
@XmlAccessorOrder
控制JAXB 绑定类中属性和字段的排序。
其他
@XmlValue、@XmlEnum、@XmlEnumValue
实现
定义xml信息
<person>
<id>111111</id>
<name>lijie</name>
<address>
<province></province>
<city></city>
<detail></detail>
</address>
<attr property="1231321" />
<items>
<item>afgafdafd</item>
<item>adfadfadfa</item>
</items>
</person>定义类
这里通过使用@XmlElementWrapper注解配合可以减少包装类的创建
@XmlRootElement(name = "person")
public class Person{
@XmlElement(name = "id")
String id;
@XmlElement(name = "name")
String name;
@XmlElement(name = "address")
Address addrss;
@XmlElement(name = "attr")
Attributes attributes;
@XmlElementWrapper(name = "items")
@XmlElement(name = "item")
List<Item> items;
}
public class Address{
@XmlElement(name = "province")
String province;
@XmlElement(name = "city")
String city;
}
public class Item{
@XmlElement(name = "item")
String des;
}
public class Attributes{
@XmlAttribute(name = "property")
String des;
}转换
xml转换为实体类
ClassPathResource resource = new ClassPathResource("xml/SnmpTrapHitRules.xml");
BufferedReader reader = new BufferedReader(new InputStreamReader(resource.getStream()));
JAXBContext jaxbContext = JAXBContext.newInstance(SnmpTrapHitRuleDto.class);
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
snmpTrapHitRuleDto = (SnmpTrapHitRuleDto) unmarshaller.unmarshal(reader);实体类转成xml
// 创建Person对象
Person person = new Person("张三", 30);
// 创建JAXBContext对象
JAXBContext jaxbContext = JAXBContext.newInstance(Person.class);
// 创建Marshaller对象
Marshaller marshaller = jaxbContext.createMarshaller();
// 设置格式化输出
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
// 将Java对象转换为XML字符串
StringWriter stringWriter = new StringWriter();
marshaller.marshal(person, stringWriter);问题
JAXB要求将注解放到对应的set/get方法上,但是由于我们可能使用的时lombok,没有显示的说明get/set方法,这时候会报错:类的两个属性具有相同名称。
解决办法是在类上加入注解 @XmlAccessorType
@XmlAccessorType
类级别的注解。定义这个类中的何种类型需要映射到XML。
解释起来有点拗口,可以通过它的属性值更好理解这个参数的意义。
- 参数 value
参数 value 可以接受4个指定值,这几个值是枚举类型,方便调用:
XmlAccessType.FIELD:映射这个类中的所有字段到XMLXmlAccessType.PROPERTY:映射这个类中的属性(get/set方法)到XMLXmlAccessType.PUBLIC_MEMBER:将这个类中的所有public的field或property同时映射到XML(默认)XmlAccessType.NONE:不映射
总结
以上为个人经验,希望能给大家一个参考,也希望大家多多支持脚本之家。
