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() {@Overridepublic void escape(char[] ch, int start,int length, boolean isAttVal,Writer writer) throws IOException {writer.write(ch, start, length);}});// 将对象转换成输出流形式的xmlmarshaller.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”;
@XmlElementprivate 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 {@XmlElementprivate String value ="111";@XmlElementprivate 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 {@XmlAttributeprivate String color ="Red";@XmlElementprivate String value ="111";@XmlElementprivate 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 {@XmlAttributeprivate String color ="Red";@XmlElementprivate String value ="111";@XmlElementprivate String name="gaoxi";@XmlElementprivate IdEntity id = new IdEntity();}//============================================@Data@XmlAccessorType(XmlAccessType.NONE)public class IdEntity {@XmlAttributeprivate String size ="1";@XmlValueprivate 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> {@Overridepublic String marshal(String arg0) throws Exception {return "<![CDATA[" + arg0 + "]]>";}@Overridepublic 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";@JacksonXmlPropertyprivate IdEntity id = new IdEntity();}@Datapublic class IdEntity {@JacksonXmlProperty(isAttribute = true)private String size = "4";@JacksonXmlTextprivate 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 {@JacksonXmlPropertyprivate 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
