XML作为老牌的数据传输格式,在项目开发中,对接老系统,比如SAP、航信的发票平台等需要使用XML进行交互。
解析或生成XML方式有以下几种:
- Java自带解析:
- Dom:生成一个文档对象
- XPath:可以直接定位到某个标签
- SAX:通过回调方法的方式解析
- JavaEE的JAXB : 极其好用
- 三方类库
- Dom4j
- XStream
- jackson-xml:基于JAXB
1.JAXB方式解析
JAXB解析XML的主要方式就是给类加注解即可,所以我们就说一下有哪些注解以及其含义。
https://blog.csdn.net/wn084/article/details/80853587 (参考)
实验的工具类代码 ```java
public class XMLUtil {
/**
* 将XML字符串转换为相应对象
*
* @param clazz 转换类型字节码
* @param xmlStr xml字符串
* @return
*/
@SuppressWarnings("unchecked")
public static Object convertXmlStrToObject(Class clazz, String xmlStr) {
Object xmlObject = null;
try {
JAXBContext context = JAXBContext.newInstance(clazz);
// 进行将Xml转成对象的核心接口
Unmarshaller unmarshaller = context.createUnmarshaller();
StringReader sr = new StringReader(xmlStr);
SAXParserFactory sax = SAXParserFactory.newInstance();
sax.setNamespaceAware(false);
XMLReader xmlReader = sax.newSAXParser().getXMLReader();
Source source = new SAXSource(xmlReader, new InputSource(sr));
xmlObject = unmarshaller.unmarshal(source);
} catch (Exception e) {
e.printStackTrace();
}
return xmlObject;
}
/**
* 将对象直接转换成String类型的 XML输出
*
* @param obj
* @return
*/
public static String convertToXml(Object obj) {
// 创建输出流
StringWriter sw = new StringWriter();
try {
// 利用jdk中自带的转换类实现
JAXBContext context = JAXBContext.newInstance(obj.getClass());
Marshaller marshaller = context.createMarshaller();
//去除<xml ...>
marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
// 格式化xml输出的格式
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
// marshaller.setProperty("com.sun.xml.bind.xmlDeclaration", Boolean.FALSE);
marshaller.setProperty("com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler",
new CharacterEscapeHandler() {
@Override
public void escape(char[] ch, int start,
int length, boolean isAttVal,
Writer writer) throws IOException {
writer.write(ch, start, length);
}
});
// 将对象转换成输出流形式的xml
marshaller.marshal(obj, sw);
} catch (JAXBException e) {
e.printStackTrace();
}
return sw.toString();
}
}
<a name="X0dFI"></a>
## 1.1@XmlRootElement
- 属性:name="根标签名称" ,namespace="命名空间url"
- 位置:标注在XML根节点映射的类上且必须
- name若不填默认为类名首字母小写,如`ClassInfo`->` <classInfo></classInfo>`
```xml
<?xml version="1.0" encoding="utf-8"?>
<Class>
<id>1</id>
<name>CS科班</name>
<stuInfo size="3">
<stu>
<id>0001</id>
<name>李四</name>
</stu>
<stu>
<id>0002</id>
<name>张已</name>
</stu>
<stu>
<id>0003</id>
<name>王强</name>
</stu>
</stuInfo>
</Class>
@Data
//若不指定,默认为类名首字母小写
@XmlRootElement(name = "Class")
public class ClassInfo {
//.....
}
1.2@XmlAccessorType
- 属性:XmlAccessType枚举类,默认值为XmlAccessType.PUBLIC_MEMBER
- XmlAccessType.PROPERTY
- 会将生成了setter/getter方法的字段自动映射到xml中。
- 如果不希望映射某个字段 ,需要在字段的stter或getter方法上加上
@XmlTransient
。 - 不存在setter/getter方法的字段,可以通过
@XmlElement
注解来将其映射为xml标签 - 千万不要在已经存在stter/getter字段上添加
@XmlElement
注解,会报异常。
- XmlAccessType.FIELD
- 会将非static、非transient字段自动映射为xml字段(即使没有setter/getter方法)
- 如果希望让static或transient字段映射到xml字段中,需要在字段上添加
@XmlElement
注解 - 允许在已经默认被映射的字段上添加
@XmlElement
注解
- XmlAccessType.PUBLIC_MEMBER(默认)
- 会将public的字段,或生成public的setter、getter方法的字段 自动映射xml
- 切记,不可以出现,字段是public,且getter、setter方法也是public的情况
- XmlAccessType.NONE
- 所有的字段均不自动映射为xml字段,除非使用
@XmlElement
注解
- 所有的字段均不自动映射为xml字段,除非使用
- XmlAccessType.PROPERTY
位置:标识在类上,这个不限制是否为xml的更节点,只要是映射到xml的类都可以,且需要指定。 ```java @Data @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) public class User {
@XmlElement private String name=”gaoxi”;
@XmlElement
private String value ="111";
}
个人感觉还是,手动在字段上加入注解更靠谱一点。
<a name="zXIob"></a>
## 1.3@XmlAccessorOrder
- 属性:XmlAccessOrder枚举类,默认是XmlAccessOrder.UNDEFINED
- **XmlAccessOrder.UNDEFINED**:不定义,按照类中字段的顺序
- **XmlAccessOrder.ALPHABETICAL**:按照字典序排列
- 位置:所有映射为XML的类上
<a name="jaWmm"></a>
## 1.4@XmlType
`@XmlType`目前我知道用法是可以指定生成xml标签的顺序,当和`@XmlAccessorOrder`同时出现后,已这个为准。<br />使用方法:
```java
@Data
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
//同时出现以@XmlType为准
@XmlType(propOrder = {
"value" , "name"
})
public class User {
@XmlElement
private String value ="111";
@XmlElement
private String name="gaoxi";
}
1.5@XmlElement
- 属性:name标签名 namespace:标签使用的命名空间
- 位置:字段、setter、getter方法上。
- 举一个namespace的使用方式:
```java @Data @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) @XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL) //同时出现以@XmlType为准 @XmlType(propOrder = { “value” , “name” }) public class User { @XmlElement(name=”value”,namespace = “http://www.baidu.com/schema“) private String value =”111”; @XmlElement private String name=”gaoxi”;<user xmlns:ns2="http://www.baidu.com/schema">
<ns2:value>111</ns2:value>
<name>gaoxi</name>
</user>
}
关注name标签,自己体会。。。
<a name="xMXNU"></a>
## <br />1.6@XmlAttribute
指定属性
```xml
<user color="Red">
<value>111</value>
<name>gaoxi</name>
</user>
@Data
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
//同时出现以@XmlType为准
@XmlType(propOrder = {
"value" , "name"
})
public class User {
@XmlAttribute
private String color ="Red";
@XmlElement
private String value ="111";
@XmlElement
private String name="gaoxi";
}
1.7@XmlTransient
指定字段不被映射
1.8@XmlValue
这个解决的问题就是遇到如下标签<id size="1">000010001</id>
这必须要封装一个对象,但是里面的value值0000100001如何取出来呢?
<user color="Red">
<id size="1">000010001</id>
<name>gaoxi</name>
<value>111</value>
</user>
@Data
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
//同时出现以@XmlType为准
/*@XmlType(propOrder = {
"value" , "name"
})*/
public class User {
@XmlAttribute
private String color ="Red";
@XmlElement
private String value ="111";
@XmlElement
private String name="gaoxi";
@XmlElement
private IdEntity id = new IdEntity();
}
//============================================
@Data
@XmlAccessorType(XmlAccessType.NONE)
public class IdEntity {
@XmlAttribute
private String size ="1";
@XmlValue
private String idValue="000010001";
}
1.9@XmlElementWrapper
用于List属性上
- 加@XmlElementWrapper()的使用
```java
1 CS科班 2 高溪 3 高吸
@Data @XmlRootElement(name = “Class”) @XmlAccessorType(XmlAccessType.NONE) public class ClassInfo {
@XmlElement(name = "id")
private String id;
@XmlElement(name = "name")
private String name;
@XmlElement(name = "teach")
private Teacher teacher;
@XmlElementWrapper(name = "sutInfo")
@XmlElement(name = "stu")
private List<Student> stu;
public ClassInfo() {
}
public ClassInfo(String id, String name) {
this.id = id;
this.name = name;
}
}
- 不加@XmlElement
```java
<Class>
<id>1</id>
<name>CS科班</name>
<stu>
<id>2</id>
<name>高溪</name>
</stu>
<stu>
<id>3</id>
<name>高吸</name>
</stu>
</Class>
@Data
@XmlRootElement(name = "Class")
@XmlAccessorType(XmlAccessType.NONE)
public class ClassInfo {
@XmlElement(name = "id")
private String id;
@XmlElement(name = "name")
private String name;
@XmlElement(name = "teach")
private Teacher teacher;
@XmlElement(name = "stu")
private List<Student> stu;
}
@XmlElementWrapper的左右就是为List包一层标签。
1.10@XmlJavaTypeAdapter
这个标签用于为某个字段添加CDATA标签
public class AdapterCdata extends XmlAdapter<String, String> {
@Override
public String marshal(String arg0) throws Exception {
return "<![CDATA[" + arg0 + "]]>";
}
@Override
public String unmarshal(String arg0) throws Exception {
return arg0;
}
}
@Data
@XmlRootElement
@XmlAccessorType(XmlAccessType.NONE)
@XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
public class User {
@XmlElement
@XmlJavaTypeAdapter(AdapterCdata.class)
private String value ="高溪--》";
}
2.Jackson的Xml解析
Jackson有个ObjectMapper,我们使用它进行JSON和对象之间的转换。其实Jackson还有一个用于Xml与对象的转换。底层好像也是引用了Jaxb。
使用方式也是学习相关的注解。
2.1实例化一个XmlMapper
依赖导入
<!--特意贴出这个依赖是因为,这个包里有jackson-core包,如果不是springboot环境需要额外引入-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
实例化(http://www.felord.cn/java-jackson-xml-json.html)
XmlMapper xmlMapper = XmlMapper.builder()
// 忽略实体类没有对应属性 如果为 true 会抛出异常
.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false)
// 忽略null
.serializationInclusion(JsonInclude.Include.NON_NULL)
// 属性使用 驼峰首字母小写
.propertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE)
.build();
2.2@JacksonXmlRootElement
等价于Jaxb中
@XmlRootElement
,但是在这里,这个注解不是必须。@Data
@JacksonXmlRootElement(localName = "myUser")
public class User {
private String name;
private String value;
}
2.3@JacksonXmlProperty
这个注解是Jaxb中
@XmlElement
和@XmlAttribute
注解的组合,非必须加上注解,模式使用字段名,可以通过配置XmlMapper
,指定生成标签名的规则。
- 实例
```xml
gaoxi
@Data @JacksonXmlRootElement(localName = “myUser”) public class User { @JacksonXmlProperty(isAttribute = false, localName = “name”) private String name = “gaoxi”; @JacksonXmlProperty(isAttribute = true, localName = “value”) private String value = “2”; }
需要映射为XML标签的字段需要满足下列条件之一
- 在字段上添加@JacksonXmlProperty注解
- 字段由setter,getter方法
<a name="OQpzY"></a>
## 2.4@JacksonXmlCData
给字段加CDATA
```xml
<myUser value="2">
<name><![CDATA[gaoxi]]></name>
</myUser>
@JacksonXmlRootElement(localName = "myUser")
public class User {
@JacksonXmlCData
@JacksonXmlProperty(isAttribute = false, localName = "name")
private String name = "gaoxi";
@JacksonXmlProperty(isAttribute = true, localName = "value")
private String value = "2";
}
2.5@JacksonXmlText
等价于@XmlValue注解的用法
<myUser value="2">
<id size="4">0001</id>
<name><![CDATA[gaoxi]]></name>
</myUser>
@JacksonXmlRootElement(localName = "myUser")
public class User {
@JacksonXmlCData
@JacksonXmlProperty(isAttribute = false, localName = "name")
private String name = "gaoxi";
@JacksonXmlProperty(isAttribute = true, localName = "value")
private String value = "2";
@JacksonXmlProperty
private IdEntity id = new IdEntity();
}
@Data
public class IdEntity {
@JacksonXmlProperty(isAttribute = true)
private String size = "4";
@JacksonXmlText
private String value="0001";
}
2.6@JacksonXmlElementWrapper
等价于@``XmlElementWrapper
的用法,但是对于List来说,Jackson默认包裹。
- 有包裹
```xml
sing dance basketball
@JacksonXmlRootElement(localName = “myUser”)
public class User {
@JacksonXmlElementWrapper(localName = “hobbies”)
@JacksonXmlProperty
private List
- 默认包裹()
```xml
<myUser>
<hobby>
<hobby>sing</hobby>
<hobby>dance</hobby>
<hobby>basketball</hobby>
</hobby>
</myUser>
@JacksonXmlRootElement(localName = "myUser")
public class User {
@JacksonXmlProperty
private List<String> hobby = Arrays.asList("sing","dance","basketball");
}
- 去除默认包裹
```xml
sing dance basketball
@JacksonXmlRootElement(localName = “myUser”)
public class User {
@JacksonXmlElementWrapper(useWrapping = false)
@JacksonXmlProperty
private List
3.注意点
1.JAXB中标签的namespace指定的是url地址
2.Jackson中标签namespace可以指定别名 (${别名}:{标签名})
4.总结
JAXB是JavaEE的规范,其使用起来也是相当顺手,Jackson-Xml操作更加的贴近用户,只保留几个最实用的注解(也足够使用了)。Jacson的CDATA用起来比Jaxb顺手。其他方面半斤八两。推荐使用Jackson