您当前的位置: 首页 >  kotlin

代码与思维

暂无认证

  • 0浏览

    0关注

    163博文

    0收益

  • 0浏览

    0点赞

    0打赏

    0留言

私信
关注
热门博文

使用 Kotlin 对 XML 文件解析、修改及创建

代码与思维 发布时间:2022-08-13 21:51:11 ,浏览量:0

一 XML 基本概念

XML 全称 ExtensibleMarkupLanguage,中文称可扩展标记语言。它是一种通用的数据交换格式,具有平台无关性、语言无关性、系统无关性的优点,给数据集成与交互带来了极大的方便。XML 在不同的语言环境中解析方式都是一样的,只不过实现的语法不同而已。

XML 可用来描述数据、存储数据、传输数据/交换数据。

XML 文档形成了一种树结构,它从"根部"开始,然后扩展到"枝叶"。DOM 又是基于树形结构的 XML 解析方式,能很好地呈现这棵树的样貌。XML 文档节点的类型主要有:

各节点定义:

Node描述子节点DocumentXML document 的根节点Element, ProcessingInstruction, DocumentType, CommentDocumentType文档属性No childrenElement元素Element, Text, Comment, ProcessingInstruction, CDATASection, EntityReferenceAttr属性Text, EntityReferenceProcessingInstruction处理指令No childrenComment注释No childrenText文本No childrenEntity实体类型项目Element, Text, Comment, ProcessingInstruction, CDATASection, EntityReference 二 XML 解析方式

一个 XML 文档的生命周期应该包括两部分:

  • 解析文档
  • 操作文档数据 那么接下来介绍如何来解析 XML 以及解析之后如何使用。

根据底层原理的不同,解析 XML 文件一般分为两种形式,一种是基于树形结构来解析的称为 DOM;另一种是基于事件流的形式称为 SAX。

2.1 DOM(Document Object Model)

DOM 是用与平台和语言无关的方式表示 XML 文档的官方 W3C 标准。是基于树形结构的 XML 解析方式,它会将整个 XML 文档读入内存并构建一个 DOM 树,基于这棵树形结构对各个节点(Node)进行操作。

优点:

  1. 允许随机读取访问数据,因为整个 Dom 树都加载到内存中
  2. 允许随机的对文档结构进行增删

缺点:

  1. 耗时,整个 XML 文档必须一次性解析完
  2. 占内存,整个 Dom 树都要加载到内存中

适用于:文档较小,且需要修改文档内容

2.1.1 DOM 解析 XML

第一步:建立一个 Stuff.xml 文件



    
        Jack
        Ma
        Hui Chuang A Li
        100000
    
    
        Pony
        Ma
        Pu Tong Jia Ting
        200000
    


第二步:DOM 解析

package com.elijah.kotlinlearning

import org.w3c.dom.Element
import org.w3c.dom.Node
import org.w3c.dom.NodeList
import java.io.File
import javax.xml.parsers.DocumentBuilderFactory

fun main(args: Array) {

    // Instantiate the Factory
    val dbf = DocumentBuilderFactory.newInstance()

    try {
        // parse XML file
        val xlmFile = File("${projectPath}/src/res/Staff.xml")
        val xmlDoc= dbf.newDocumentBuilder().parse(xlmFile)
        xmlDoc.documentElement.normalize()

        println("Root Element :" + xmlDoc.documentElement.nodeName)
        println("--------")

        // get 
        val staffList: NodeList = xmlDoc.getElementsByTagName("staff")

        for (i in 0 until staffList.length) {
            var staffNode = staffList.item(i)

            if (staffNode.nodeType === Node.ELEMENT_NODE) {

                val element = staffNode as Element

                // get staff's attribute
                val id = element.getAttribute("id")

                // get text
                val firstname = element.getElementsByTagName("firstname").item(0).textContent
                val lastname = element.getElementsByTagName("lastname").item(0).textContent
                val nickname = element.getElementsByTagName("nickname").item(0).textContent

                val salaryNodeList = element.getElementsByTagName("salary")
                val salary = salaryNodeList.item(0).textContent

                // get salary's attribute
                val currency = salaryNodeList.item(0).attributes.getNamedItem("currency").textContent

                println("Current Element : ${staffNode.nodeName}")
                println("Staff Id : $id")
                println("First Name: $firstname")
                println("Last Name: $lastname")
                println("Nick Name: $nickname")
                println("Salary [Currency] : ${salary.toLong()} [$currency]")
            }
        }
    } catch (e: Throwable) {
        e.printStackTrace()
    }
}

第三步:解析结果输出

Root Element :company
--------
Current Element : staff
Staff Id : 1001
First Name: Jack
Last Name: Ma
Nick Name: Hui Chuang A Li
Salary [Currency] : 100000 [USD]
Current Element : staff
Staff Id : 2001
First Name: Pony
Last Name: Ma
Nick Name: Pu Tong Jia Ting
Salary [Currency] : 200000 [RMB]

2.1.2 DOM 创建、生成 XML

第一步:创建新的 XML 并填充内容

package com.elijah.kotlinlearning

import org.w3c.dom.Document
import org.w3c.dom.Element
import java.io.File
import javax.xml.parsers.DocumentBuilderFactory
import javax.xml.transform.OutputKeys
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult

fun main(args: Array) {

    // Instantiate the Factory
    val docFactory = DocumentBuilderFactory.newInstance()

    try {
        // root elements
        val docBuilder = docFactory.newDocumentBuilder()
        val doc = docBuilder.newDocument()
        val rootElement: Element = doc.createElement("company")
        doc.appendChild(rootElement)

        // add xml elements: staff 1001
        val staff = doc.createElement("staff")
        staff.setAttribute("id", "1001")

        // set staff 1001's attribute
        val firstname = doc.createElement("firstname")
        firstname.textContent = "Jack"
        staff.appendChild(firstname)
        val lastname = doc.createElement("lastname")
        lastname.textContent = "Ma"
        staff.appendChild(lastname)
        val nickname = doc.createElement("nickname")
        nickname.textContent = "Hui Chuang A Li"
        staff.appendChild(nickname)
        val salary: Element = doc.createElement("salary")
        salary.setAttribute("currency", "USD")
        salary.textContent = "100000"
        staff.appendChild(salary)
        rootElement.appendChild(staff)

        // add xml elements: staff 1002
        val staff2: Element = doc.createElement("staff")
        rootElement.appendChild(staff2)
        staff2.setAttribute("id", "1002")

        // set staff 1002's attribute
        val firstname2 = doc.createElement("firstname")
        firstname2.textContent = "Pony"
        staff2.appendChild(firstname2)
        val lastname2 = doc.createElement("lastname")
        lastname2.textContent = "Ma"
        staff2.appendChild(lastname2)
        val nickname2 = doc.createElement("nickname")
        nickname2.textContent = "Pu Tong Jia Ting"
        staff2.appendChild(nickname2)
        val salary2= doc.createElement("salary")
        salary2.setAttribute("currency", "RMB")
        salary2.textContent = "200000"
        staff2.appendChild(salary2)
        rootElement.appendChild(staff2)

        val newXmlFile = File("${projectPath}/src/res/", "generatedXml.xml")

        // write doc to new xml file
        generateXml(doc, newXmlFile)
    } catch (e: Throwable) {
        e.printStackTrace()
    }
}

// write doc to new xml file
private fun generateXml(doc: Document, file: File) {
    // Instantiate the Transformer
    val transformerFactory = TransformerFactory.newInstance()
    val transformer = transformerFactory.newTransformer()

    // pretty print
    transformer.setOutputProperty(OutputKeys.INDENT, "yes")
    val source = DOMSource(doc)
    val result = StreamResult(file)
    transformer.transform(source, result)
}

第二步:生成 XML 文件 generatedXml.xml



    
        Jack
        Ma
        Hui Chuang A Li
        100000
    
    
        Pony
        Ma
        Pu Tong Jia Ting
        200000
    


2.2 SAX(Simple API for XML)

SAX 处理的特点是基于事件流的。分析能够立即开始,而不是等待所有的数据被处理。而且,由于应用程序只是在读取数据时检查数据,因此不需要将数据存储在内存中。这对于大型文档来说是个巨大的优点。

优点:

  1. 访问能够立即进行,不需要等待所有数据被加载
  2. 只在读取数据时检查数据,不需要保存在内存中
  3. 占用内存少,不需要将整个数据都加载到内存中
  4. 允许注册多个 Handler,可以用来解析文档内容,DTD 约束等等

缺点:

  1. 需要应用程序自己负责 TAG 的处理逻辑(例如维护父/子关系等),文档越复杂程序就越复杂
  2. 单向导航,无法定位文档层次,很难同时访问同一文档的不同部分数据,不支持 XPath
  3. 不能随机访问 xml 文档,不支持原地修改 xml

适用于: 文档较大,只需要读取文档数据。

2.2.1 SAX 解析 XML

第一步:新建 ContentHandler 解析类

package com.elijah.kotlinlearning

import org.xml.sax.Attributes
import org.xml.sax.helpers.DefaultHandler

class ContentHandler: DefaultHandler(){

    private var nodeName :String? = null           // 当前节点名
    private lateinit var firstname: StringBuilder  // 属性:firstname
    private lateinit var lastname: StringBuilder   // 属性:lastname
    private lateinit var nickname: StringBuilder   // 属性:nickname
    private lateinit var salary: StringBuilder     // 属性:salary

    // 开始解析文档
    override fun startDocument() {
        firstname = StringBuilder()
        lastname = StringBuilder()
        nickname = StringBuilder()
        salary = StringBuilder()
    }

    // 开始解析节点
    override fun startElement(
        uri: String?,
        localName: String?,
        qName: String?,
        attributes: Attributes?
    ) {
        nodeName = localName
    }

    // 开始解析字符串
    override fun characters(ch: CharArray?, start: Int, length: Int) {
        // 判断节点名称
        when (nodeName) {
            "firstname" -> {
                firstname.append(ch, start, length)
            }
            "lastname" -> {
                lastname.append(ch, start, length)
            }
            "nickname" -> {
                nickname.append(ch, start, length)
            }
            "salary" -> {
                salary.append(ch, start, length)
            }
        }
    }

    // 结束解析节点
    override fun endElement(uri: String?, localName: String?, qName: String?) {
        // 打印出来解析结果
        if (localName == "staff") {
            println("Staff is : $nodeName")
            println("First Name: ${firstname.toString()}")
            println("Last Name: ${lastname.toString()}")
            println("Nick Name: ${nickname.toString()}")
            println("Salary [Currency] : ${salary.toString()}")

            // 清空, 不妨碍下一个 staff 节点的解析
            firstname.clear()
            lastname.clear()
            nickname.clear()
            salary.clear()
        }
    }

    // 结束解析文档
    override fun endDocument() {
        super.endDocument()
    }
}

第二步:新建解析器对指定 XML 进行解析

package com.elijah.kotlinlearning

import org.xml.sax.InputSource
import java.io.File
import javax.xml.parsers.SAXParserFactory

fun main(args: Array) {
    try{
        // 新建解析器工厂
        val saxParserFactory = SAXParserFactory.newInstance()
        // 通过解析器工厂获得解析器对象
        val saxParser = saxParserFactory.newSAXParser()
        // 获得 xmlReader
        val xmlReader = saxParser.xmlReader
        // 设置解析器中的解析类
        xmlReader.contentHandler = ContentHandler()
        // 设置解析内容
        val inputStream = File("${projectPath}/src/res/Staff.xml").inputStream()
        xmlReader.parse(InputSource(inputStream))
    } catch(e: Throwable){
        e.printStackTrace()
    }
}

作者:话唠扇贝 链接:https://juejin.cn/post/7131018900002570276 来源:稀土掘金

关注
打赏
1665387627
查看更多评论
立即登录/注册

微信扫码登录

0.0421s