当前位置: 首页 > 工具软件 > JAXB > 使用案例 >

复杂xml解析(JAXB)

马渊
2023-12-01

一. 背景

近期因为结束无人机航线规划, 大量结束kml文件(近似xml的文件), 需要解析复杂xml, 了解到一些xml的解析工具,

  1. 学习关键点:
  • 解析
  • 编写
  • 约束文件(DTD文件[.dtd], Schema文件[.xsd]), 即常用的命名空间
  • 验证xml是否符合约束文件
  1. 常见解析工具
  • JDOM(不常用): 底层也是JAXP
  • DOM4J: java比较流行的框架所选
  • JAXB: 特点使用注解, 使用简单
  • JAXP: 原生, 按标签解析或通过SAX进行解析, SAX与easy excel差不多都是按行解析, 如果不做修改, 这种方法占用内存较少

DOM4J需要单独引入依赖, 其他三种, 作者目前使用jdk8完全可以使用(根据不同版本会有不同)

  1. xml的验证
  • DOM4J
  • JAXP

二. 使用

目前遇到的都是复杂xml, 主要使用JAXB解析文件, 其他几种都需要写代码, 匹配标签进行实体类的封装

1. 注解使用

参考博客:JAXB常用注解讲解(超详细)

@XmlRootElement

  • 作用和用法:
    类级别的注解,将类映射为根元素
  • 属性:
    该注解含有name和namespace两个属性。
    • namespace属性用于指定生成的元素所属的命名空间。
    • name属性用于指定生成元素的名字,若不指定则默认使用类名小写作为元素名。

注: 如果类型中使用多个命名空间, 建议所有@XmlRootElement、@Xmllement注解表明命名空间

@XmlElement

  • 作用和用法:
    字段,方法,参数级别的注解。该注解可以将被注解的字段(非静态),或者被注解的get/set方法对应的字段映射为本地元素,也就是子元素。
    默认使用字段名或get/set方法去掉前缀剩下部分小写作为元素名(在字段名和get/set方法符合命名规范的情况下)。

  • 属性:
    该注解的属性常用的属性有有:name、nillable、required、namespace、defaultValue

    • name属性可以指定生成元素的名字,同@XmlRootElement注解的name属性一样,不再举例。
    • namespace属性用于指定生成的元素所属的命名空间。
    • nillable属性可以指定元素的文本值是否可以为空,默认为false。

@XmlAttribute

  • 作用和用法:
    字段和方法级别的注解。该注解会将字段或get/set方法对应的字段映射成本类对应元素的属性,属性名默认使用字段名或get/set方法去掉前缀剩下部分首字母小写(在字段名和get/set方法符合命名规范的情况下)。
  • 属性:
    该注解有name,required,namespace三个属性。用法和@XmlElement注解相同,不再举例。

@XmlTransient

  • 作用和用法:
    类,字段,方法级别的注解。可使JAXB在映射xml元素时忽略被注解的类,字段,get/set对应字段。需要注意的是该注解与所有其他JAXB注释相互排斥,也就是说与其他注释连用就会报错。修改上面例子:

@XmlAccessorType

  • 作用和用法:
    包和类级别的注解。javaEE的API对该注解的解释是:控制字段是否被默认序列化。通俗来讲,就是决定哪些字段或哪些get/set方法对应的字段会被映射为xml元素,需要注意的是字段或get/set方法的访问权限(public/private)会影响字段是否被映射为xml元素,下面会详细讲解。

注: 主要与@XmlElement位置有关

关键属性:

  • XmlAccessType.PROPERTY: 默认, 一般@XmlElement标注在类中getter或setter方法上
  • XmlAccessType.FIELD: 一般@XmlElement标注在类属性上

@XmlSeeAlso

  • 作用和用法
    类注解, 用在父类中, 表明其下所有子类

@XmlType

  • 作用和用法
    类注解, 用于表明一个类的引用, 以及类下属性的解析顺序, 一般用于继承类的识别
    -属性
    name: 引用名称
    propOrder: 属性组

@XmlElementRef

  • 作用和用法
    属性或方法注解, 用与表明引用的类, 主要与@XmlType配合使用, name一致, 一般用于继承类的引用
    -属性
    name: @XmlType的name
    namespace: 命名空间

2. 示例

2.1 基本类

Template类

@Data
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "TemplateType", propOrder = {
  "document"
})
@XmlRootElement(name = "template", namespace = "http://www.abc.cn")
public class Template implements Cloneable {
  @XmlElementRef(name = "Document", namespace = "http://www.abc.cn")
  protected BaseDocumentType document;
}

BaseDocument类

@Data
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "BaseDocumentType", propOrder = {
  "author",
  "createTime",
  "updateTime",
})
@XmlSeeAlso({
  TemplateDocument.class,
  WaylineDocument.class
})
@XmlRootElement(name = "Document", namespace = "http://www.abc.cn")
public abstract class BaseDocument {
  @XmlElement(name = "author", namespace = "http://www.abc.cn")
  protected String author;

  @XmlElement(name = "createTime", namespace = "http://www.abc.cn")
  protected Long createTime;

  @XmlElement(name = "updateTime", namespace = "http://www.abc.cn")
  protected Long updateTime;
}

TemplateDocument类

@Data
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "TemplateDocumentType", propOrder = {
  "folder"
})
@XmlRootElement(name = "Document", namespace = "http://www.abc.cn")
public abstract class TemplateDocument extends BaseDocument {
  @XmlElement(name = "Folder", namespace = "http://www.def.cn")
  protected String folder;
}

WaylineDocument类

@Data
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "TemplateDocumentType", propOrder = {
  "folder"
})
@XmlRootElement(name = "Document", namespace = "http://www.abc.cn")
public abstract class WaylineDocument extends BaseDocument {
  @XmlElement(name = "Wayline", namespace = "http://www.def.cn")
  protected String Wayline;
}

@XmlElementRef(name = “Document”, namespace = “http://www.abc.cn”)
protected BaseDocumentType document;
这段代码, 会根据@XmlSeeAlso标识的类, 根据标签不同自动匹配

2.2工具类

@Slf4j
public class KMLUtil {
  /** xml 转 对象 */
  public static Template parse(Class<?> clazz, InputStream inputStream) throws JAXBException {
    log.debug("[param:parse]==>clazz:{}, inputStream:{}", clazz, inputStream);
    JAXBContext jaxbContext = JAXBContext.newInstance(clazz);
    Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
    return (WayLine)unmarshaller.unmarshal(inputStream);
  }

  /** 对象 转 xml */
  public static void format(Template template, String path) throws JAXBException, IOException {
    log.debug("[param:parse]==>wayline:{}, path:{}", template, path);
    JAXBContext jaxbContext = JAXBContext.newInstance(template.getClass());
    Marshaller marshaller = jaxbContext.createMarshaller();
    marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true);
    // 禁用默认的XML声明
    marshaller.setProperty(Marshaller.JAXB_FRAGMENT, true);

    File file = new File(path);
    if (!file.getParentFile().exists()) {
      file.getParentFile().mkdirs();
    }
    if (!file.exists()) {
      file.createNewFile();
    } else {
      file.delete();
      file.createNewFile();
    }

    StringWriter sw = new StringWriter();
    sw.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n");
    marshaller.marshal(wayline, sw);
    FileWriter fw = new FileWriter(path);
    fw.write(sw.toString());
    fw.flush();
    fw.close();
  }

}
 类似资料: