XML作为老牌的数据传输格式,在项目开发中,对接老系统,比如SAP、航信的发票平台等需要使用XML进行交互。
解析或生成XML方式有以下几种:

  1. Java自带解析:
    1. Dom:生成一个文档对象
    2. XPath:可以直接定位到某个标签
    3. SAX:通过回调方法的方式解析
  2. JavaEE的JAXB : 极其好用
  3. 三方类库
    1. Dom4j
    2. XStream
    3. jackson-xml:基于JAXB

      1.JAXB方式解析

      JAXB解析XML的主要方式就是给类加注解即可,所以我们就说一下有哪些注解以及其含义。
      https://blog.csdn.net/wn084/article/details/80853587 (参考)
      实验的工具类代码 ```java

public class XMLUtil {

  1. /**
  2. * 将XML字符串转换为相应对象
  3. *
  4. * @param clazz 转换类型字节码
  5. * @param xmlStr xml字符串
  6. * @return
  7. */
  8. @SuppressWarnings("unchecked")
  9. public static Object convertXmlStrToObject(Class clazz, String xmlStr) {
  10. Object xmlObject = null;
  11. try {
  12. JAXBContext context = JAXBContext.newInstance(clazz);
  13. // 进行将Xml转成对象的核心接口
  14. Unmarshaller unmarshaller = context.createUnmarshaller();
  15. StringReader sr = new StringReader(xmlStr);
  16. SAXParserFactory sax = SAXParserFactory.newInstance();
  17. sax.setNamespaceAware(false);
  18. XMLReader xmlReader = sax.newSAXParser().getXMLReader();
  19. Source source = new SAXSource(xmlReader, new InputSource(sr));
  20. xmlObject = unmarshaller.unmarshal(source);
  21. } catch (Exception e) {
  22. e.printStackTrace();
  23. }
  24. return xmlObject;
  25. }
  26. /**
  27. * 将对象直接转换成String类型的 XML输出
  28. *
  29. * @param obj
  30. * @return
  31. */
  32. public static String convertToXml(Object obj) {
  33. // 创建输出流
  34. StringWriter sw = new StringWriter();
  35. try {
  36. // 利用jdk中自带的转换类实现
  37. JAXBContext context = JAXBContext.newInstance(obj.getClass());
  38. Marshaller marshaller = context.createMarshaller();
  39. //去除<xml ...>
  40. marshaller.setProperty(Marshaller.JAXB_FRAGMENT, Boolean.TRUE);
  41. // 格式化xml输出的格式
  42. marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
  43. // marshaller.setProperty("com.sun.xml.bind.xmlDeclaration", Boolean.FALSE);
  44. marshaller.setProperty("com.sun.xml.internal.bind.marshaller.CharacterEscapeHandler",
  45. new CharacterEscapeHandler() {
  46. @Override
  47. public void escape(char[] ch, int start,
  48. int length, boolean isAttVal,
  49. Writer writer) throws IOException {
  50. writer.write(ch, start, length);
  51. }
  52. });
  53. // 将对象转换成输出流形式的xml
  54. marshaller.marshal(obj, sw);
  55. } catch (JAXBException e) {
  56. e.printStackTrace();
  57. }
  58. return sw.toString();
  59. }

}

  1. <a name="X0dFI"></a>
  2. ## 1.1@XmlRootElement
  3. - 属性:name="根标签名称" ,namespace="命名空间url"
  4. - 位置:标注在XML根节点映射的类上且必须
  5. - name若不填默认为类名首字母小写,如`ClassInfo`->` <classInfo></classInfo>`
  6. ```xml
  7. <?xml version="1.0" encoding="utf-8"?>
  8. <Class>
  9. <id>1</id>
  10. <name>CS科班</name>
  11. <stuInfo size="3">
  12. <stu>
  13. <id>0001</id>
  14. <name>李四</name>
  15. </stu>
  16. <stu>
  17. <id>0002</id>
  18. <name>张已</name>
  19. </stu>
  20. <stu>
  21. <id>0003</id>
  22. <name>王强</name>
  23. </stu>
  24. </stuInfo>
  25. </Class>
  1. @Data
  2. //若不指定,默认为类名首字母小写
  3. @XmlRootElement(name = "Class")
  4. public class ClassInfo {
  5. //.....
  6. }

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的更节点,只要是映射到xml的类都可以,且需要指定。 ```java @Data @XmlRootElement @XmlAccessorType(XmlAccessType.NONE) public class User {

    @XmlElement private String name=”gaoxi”;

  1. @XmlElement
  2. private String value ="111";

}

  1. 个人感觉还是,手动在字段上加入注解更靠谱一点。
  2. <a name="zXIob"></a>
  3. ## 1.3@XmlAccessorOrder
  4. - 属性:XmlAccessOrder枚举类,默认是XmlAccessOrder.UNDEFINED
  5. - **XmlAccessOrder.UNDEFINED**:不定义,按照类中字段的顺序
  6. - **XmlAccessOrder.ALPHABETICAL**:按照字典序排列
  7. - 位置:所有映射为XML的类上
  8. <a name="jaWmm"></a>
  9. ## 1.4@XmlType
  10. `@XmlType`目前我知道用法是可以指定生成xml标签的顺序,当和`@XmlAccessorOrder`同时出现后,已这个为准。<br />使用方法:
  11. ```java
  12. @Data
  13. @XmlRootElement
  14. @XmlAccessorType(XmlAccessType.NONE)
  15. @XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
  16. //同时出现以@XmlType为准
  17. @XmlType(propOrder = {
  18. "value" , "name"
  19. })
  20. public class User {
  21. @XmlElement
  22. private String value ="111";
  23. @XmlElement
  24. private String name="gaoxi";
  25. }

1.5@XmlElement

  • 属性:name标签名 namespace:标签使用的命名空间
  • 位置:字段、setter、getter方法上。
  • 举一个namespace的使用方式:
    1. <user xmlns:ns2="http://www.baidu.com/schema">
    2. <ns2:value>111</ns2:value>
    3. <name>gaoxi</name>
    4. </user>
    ```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”;

}

  1. 关注name标签,自己体会。。。
  2. <a name="xMXNU"></a>
  3. ## <br />1.6@XmlAttribute
  4. 指定属性
  5. ```xml
  6. <user color="Red">
  7. <value>111</value>
  8. <name>gaoxi</name>
  9. </user>
  1. @Data
  2. @XmlRootElement
  3. @XmlAccessorType(XmlAccessType.NONE)
  4. @XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
  5. //同时出现以@XmlType为准
  6. @XmlType(propOrder = {
  7. "value" , "name"
  8. })
  9. public class User {
  10. @XmlAttribute
  11. private String color ="Red";
  12. @XmlElement
  13. private String value ="111";
  14. @XmlElement
  15. private String name="gaoxi";
  16. }

1.7@XmlTransient

指定字段不被映射

1.8@XmlValue

这个解决的问题就是遇到如下标签
<id size="1">000010001</id>
这必须要封装一个对象,但是里面的value值0000100001如何取出来呢?

  1. <user color="Red">
  2. <id size="1">000010001</id>
  3. <name>gaoxi</name>
  4. <value>111</value>
  5. </user>
  1. @Data
  2. @XmlRootElement
  3. @XmlAccessorType(XmlAccessType.NONE)
  4. @XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
  5. //同时出现以@XmlType为准
  6. /*@XmlType(propOrder = {
  7. "value" , "name"
  8. })*/
  9. public class User {
  10. @XmlAttribute
  11. private String color ="Red";
  12. @XmlElement
  13. private String value ="111";
  14. @XmlElement
  15. private String name="gaoxi";
  16. @XmlElement
  17. private IdEntity id = new IdEntity();
  18. }
  19. //============================================
  20. @Data
  21. @XmlAccessorType(XmlAccessType.NONE)
  22. public class IdEntity {
  23. @XmlAttribute
  24. private String size ="1";
  25. @XmlValue
  26. private String idValue="000010001";
  27. }

1.9@XmlElementWrapper

用于List属性上

  • 加@XmlElementWrapper()的使用 ```java 1 CS科班 2 高溪 3 高吸

@Data @XmlRootElement(name = “Class”) @XmlAccessorType(XmlAccessType.NONE) public class ClassInfo {

  1. @XmlElement(name = "id")
  2. private String id;
  3. @XmlElement(name = "name")
  4. private String name;
  5. @XmlElement(name = "teach")
  6. private Teacher teacher;
  7. @XmlElementWrapper(name = "sutInfo")
  8. @XmlElement(name = "stu")
  9. private List<Student> stu;
  10. public ClassInfo() {
  11. }
  12. public ClassInfo(String id, String name) {
  13. this.id = id;
  14. this.name = name;
  15. }

}

  1. - 不加@XmlElement
  2. ```java
  3. <Class>
  4. <id>1</id>
  5. <name>CS科班</name>
  6. <stu>
  7. <id>2</id>
  8. <name>高溪</name>
  9. </stu>
  10. <stu>
  11. <id>3</id>
  12. <name>高吸</name>
  13. </stu>
  14. </Class>
  15. @Data
  16. @XmlRootElement(name = "Class")
  17. @XmlAccessorType(XmlAccessType.NONE)
  18. public class ClassInfo {
  19. @XmlElement(name = "id")
  20. private String id;
  21. @XmlElement(name = "name")
  22. private String name;
  23. @XmlElement(name = "teach")
  24. private Teacher teacher;
  25. @XmlElement(name = "stu")
  26. private List<Student> stu;
  27. }

@XmlElementWrapper的左右就是为List包一层标签。

1.10@XmlJavaTypeAdapter

这个标签用于为某个字段添加CDATA标签

  1. public class AdapterCdata extends XmlAdapter<String, String> {
  2. @Override
  3. public String marshal(String arg0) throws Exception {
  4. return "<![CDATA[" + arg0 + "]]>";
  5. }
  6. @Override
  7. public String unmarshal(String arg0) throws Exception {
  8. return arg0;
  9. }
  10. }
  11. @Data
  12. @XmlRootElement
  13. @XmlAccessorType(XmlAccessType.NONE)
  14. @XmlAccessorOrder(XmlAccessOrder.ALPHABETICAL)
  15. public class User {
  16. @XmlElement
  17. @XmlJavaTypeAdapter(AdapterCdata.class)
  18. private String value ="高溪--》";
  19. }

2.Jackson的Xml解析

Jackson有个ObjectMapper,我们使用它进行JSON和对象之间的转换。其实Jackson还有一个用于Xml与对象的转换。底层好像也是引用了Jaxb。
使用方式也是学习相关的注解。

2.1实例化一个XmlMapper

  1. 依赖导入

    1. <!--特意贴出这个依赖是因为,这个包里有jackson-core包,如果不是springboot环境需要额外引入-->
    2. <dependency>
    3. <groupId>org.springframework.boot</groupId>
    4. <artifactId>spring-boot-starter-web</artifactId>
    5. </dependency>
    6. <dependency>
    7. <groupId>com.fasterxml.jackson.dataformat</groupId>
    8. <artifactId>jackson-dataformat-xml</artifactId>
    9. </dependency>
  2. 实例化(http://www.felord.cn/java-jackson-xml-json.html

    1. XmlMapper xmlMapper = XmlMapper.builder()
    2. // 忽略实体类没有对应属性 如果为 true 会抛出异常
    3. .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES,false)
    4. // 忽略null
    5. .serializationInclusion(JsonInclude.Include.NON_NULL)
    6. // 属性使用 驼峰首字母小写
    7. .propertyNamingStrategy(PropertyNamingStrategy.LOWER_CAMEL_CASE)
    8. .build();

    2.2@JacksonXmlRootElement

    等价于Jaxb中@XmlRootElement,但是在这里,这个注解不是必须。

    1. @Data
    2. @JacksonXmlRootElement(localName = "myUser")
    3. public class User {
    4. private String name;
    5. private String value;
    6. }

    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”; }

  1. 需要映射为XML标签的字段需要满足下列条件之一
  2. - 在字段上添加@JacksonXmlProperty注解
  3. - 字段由settergetter方法
  4. <a name="OQpzY"></a>
  5. ## 2.4@JacksonXmlCData
  6. 给字段加CDATA
  7. ```xml
  8. <myUser value="2">
  9. <name><![CDATA[gaoxi]]></name>
  10. </myUser>
  11. @JacksonXmlRootElement(localName = "myUser")
  12. public class User {
  13. @JacksonXmlCData
  14. @JacksonXmlProperty(isAttribute = false, localName = "name")
  15. private String name = "gaoxi";
  16. @JacksonXmlProperty(isAttribute = true, localName = "value")
  17. private String value = "2";
  18. }

2.5@JacksonXmlText

等价于@XmlValue注解的用法

  1. <myUser value="2">
  2. <id size="4">0001</id>
  3. <name><![CDATA[gaoxi]]></name>
  4. </myUser>
  5. @JacksonXmlRootElement(localName = "myUser")
  6. public class User {
  7. @JacksonXmlCData
  8. @JacksonXmlProperty(isAttribute = false, localName = "name")
  9. private String name = "gaoxi";
  10. @JacksonXmlProperty(isAttribute = true, localName = "value")
  11. private String value = "2";
  12. @JacksonXmlProperty
  13. private IdEntity id = new IdEntity();
  14. }
  15. @Data
  16. public class IdEntity {
  17. @JacksonXmlProperty(isAttribute = true)
  18. private String size = "4";
  19. @JacksonXmlText
  20. private String value="0001";
  21. }

2.6@JacksonXmlElementWrapper

等价于@``XmlElementWrapper的用法,但是对于List来说,Jackson默认包裹。

  • 有包裹 ```xml sing dance basketball

@JacksonXmlRootElement(localName = “myUser”) public class User { @JacksonXmlElementWrapper(localName = “hobbies”) @JacksonXmlProperty private List hobby = Arrays.asList(“sing”,”dance”,”basketball”); }

  1. - 默认包裹()
  2. ```xml
  3. <myUser>
  4. <hobby>
  5. <hobby>sing</hobby>
  6. <hobby>dance</hobby>
  7. <hobby>basketball</hobby>
  8. </hobby>
  9. </myUser>
  10. @JacksonXmlRootElement(localName = "myUser")
  11. public class User {
  12. @JacksonXmlProperty
  13. private List<String> hobby = Arrays.asList("sing","dance","basketball");
  14. }
  • 去除默认包裹 ```xml sing dance basketball

@JacksonXmlRootElement(localName = “myUser”) public class User { @JacksonXmlElementWrapper(useWrapping = false) @JacksonXmlProperty private List hobby = Arrays.asList(“sing”,”dance”,”basketball”); }

```

3.注意点

1.JAXB中标签的namespace指定的是url地址
2.Jackson中标签namespace可以指定别名 (${别名}:{标签名})

4.总结

JAXB是JavaEE的规范,其使用起来也是相当顺手,Jackson-Xml操作更加的贴近用户,只保留几个最实用的注解(也足够使用了)。Jacson的CDATA用起来比Jaxb顺手。其他方面半斤八两。推荐使用Jackson