SwiftUI学习笔记【XML解析】

2,872 阅读5分钟

如果大佬没留下赞,我祝你一生富贵,美女如云。

前言

xml解析百度很多都是OC,我们来干一波Swift吧,群里大佬比较多,语法不会的问大佬,编译器不会的问大佬,炒股的问大佬....Q裙:730772561 大佬太多,不慌。


今日提示:知耻下问,后发先至。

一、XMLParser的了解

对于接触过IOS解析XML的应该很多吧...我是写Android的我司项目会涉及到很多xml解析导出给CAD然后CAD解析完成之后进行绘制编辑等。原生Android有很多对于XML的解析方式[SAX,Pull,DOM等],Flutter的对于XML解析很少了之前就见过XML这个库。

  • 在使用最原始的XMLParser之前,我百度看过其他人封装的解析器,在分离方面显的很麻烦。对于类的构建要求比较高,如果涉及到上千节点的的xml那就太过于麻烦。我们先来最原始基本的,说不定是最舒服的方式。在Swift里面XMLParser为我们开发提供了便利,如下我们看看源码也就三个初始化构造函数init(..),一个开始解析的方法parse(),一个解析器委托delegate,其实最主要的就是解析器委托了,所有解析的过程都交付委托给解析器委托delegate工具了。
open class XMLParser : NSObject {

    //url初始化也可以
    public convenience init?(contentsOf url: URL)
    //根据文件字节bytes data初始化也可以
    public init(data: Data)
    //stream初始化也可以
    @available(iOS 5.0, *) 
    public convenience init(stream: InputStream)

    //XML解析器委托
    unowned(unsafe) open var delegate: XMLParserDelegate?
    
    open var shouldProcessNamespaces: Bool

    open var shouldReportNamespacePrefixes: Bool
    
    @available(iOS 8.0, *)
    open var externalEntityResolvingPolicy: XMLParser.ExternalEntityResolvingPolicy
    
    @available(iOS 8.0, *)
    open var allowedExternalEntityURLs: Set<URL>?

    open func parse() -> Bool

    open func abortParsing()
 
    open var parserError: Error? { get }
 
    open var shouldResolveExternalEntities: Bool
}

二、新建xml文件且导入项目

1.新建.xml文件然后写入你想解析的节点或者已有的及其复杂的.xml文件。我的如下(粘贴的):

<?xml version="1.0" encoding="utf-8"?>
<Users>
    <User id="101">
        <name>航歌</name>
        <tel>
            <mobile>1234567</mobile>
            <home>025-8100000</home>
        </tel>
    </User>
    <User id="102">
        <name>hangge</name>
        <tel>
            <mobile>8989889</mobile>
            <home>025-8122222</home>
        </tel>
    </User>
</Users>

2.导入xml

如图操作即可和上一篇SwiftUI学习笔记[Sqlite]一样一样的。

三.解析xml

1.新建结构

我们根据xml的内容来新建数据模型对于数据模型是你的需求所决定的。这里也就演变出不同的解析过程对于赋值方式的不同。有些人的数据模型如下说我只想要个User其他的内部节点作为其内部属性:

//struct也行
struct MyUser{
    var id: String?  //编号
    var name: String? //姓名
    var tel:Tel?//电话
    class Tel {
        var mobile: String? //手机
        var home: String? //固话
    }
}
//class 也行都为来储存数据。
class MyUser {
    var id: String?  //编号
    var name: String? //姓名
    var tel:Tel?//电话
    class Tel {
        var mobile: String? //手机
        var home: String? //固话
    }
}

有些人需要分开User和Tel这是一个举例。但是在实际中我们对于父子节点来说是其类-->属性,但是可能xml作为接口文件。而当前页面只需要Tel不需要整体,解析那更简单了。

    class Tel {
        var mobile: String? //手机
        var home: String? //固话
    }

2.解析器委托进行解析。

XML解析器委托:XMLParserDelegate作为代理接口协议,定义了解析过程所

【1】:节点属性解析

解析器,必须每个节点的开始和结束都需要提供接口去解析成模型,如下代码:每次到一个节点开始都会调用下面方法-->这时我们进行创建对应的数据类class或者模型结构struct.

<?xml version="1.0" encoding="utf-8"?>
<Users xmlns ="http://www.example.com/DEFAULT">
    <User id="101" uid="2021 come on">
        <name>航歌</name>
        <tel>
            <mobile>1234567</mobile>
            <home>025-8100000</home>
        </tel>
    </User>
    <User id="102">
        <name>hangge</name>
        <tel>
            <mobile>8989889</mobile>
            <home>025-8122222</home>
        </tel>
    </User>
</Users>

elementNamenamespaceURIqualifiedNameattributes
每个节点起始名称命名空间限定名称属性
如上面:User,User,tel等
有自节点,而不是字符节点
如:<name>航歌</name>
相当于身份标示命
名,学过前端的都明白
XML第一节课估计
就是
本地名?百度百度😂节点属性是如上id="101"
uid="2021 come on",
而不是子节点
<elementName >
  <otherElementName>
xmlns ="www.exa
mple.com/DEFAULT"
😂attributeDict["id"],attributeDict["uid"]
     // 遇到一个开始标签时调用
    func parser(_ parser: XMLParser, didStartElement elementName: String,
                   namespaceURI: String?, qualifiedName qName: String?,
                   attributes attributeDict: [String : String] = [:]) {
                   }
  • 进行创建对应的数据类或者模型结构.
    // 遇到一个开始标签时调用
    func parser(_ parser: XMLParser, didStartElement elementName: String,
                   namespaceURI: String?, qualifiedName qName: String?,
                   attributes attributeDict: [String : String] = [:]) {
           currentElement = elementName
        //每个节点名的开始就是我们要不要实例化模型或者类。
        switch elementName {
            case "User":
                //创建一个新用户对象
                user = MyUser()
                //保存下id
                user.id = attributeDict["id"]
            case "tel":
                tel = MyUser.Tel()
            default:
              break
            
       }
    }

【2】:解析字符节点
foundCharacters
每个字符节点内容
<name>航歌<name>
   // 遇到字符串时调用
     func parser(_ parser: XMLParser, foundCharacters string: String) {
           //这里只是去去空格过滤字符的。别感觉到太难了😄
           let data = string.trimmingCharacters(in: .whitespacesAndNewlines)
           //接下来每遇到一个字符,将该字符追加到相应的 property 中
           switch currentElement{
           case "name":
               user.name = user.name ?? "" + data
           case "mobile":
               tel.mobile = tel.mobile ?? "" +  data
           case "home":
               tel.home = tel.home ?? "" + data
           default:
               break
           }
       }                

import UIKit
import SwiftEventBus

class XMLUtils:NSObject, XMLParserDelegate{
    //保存最终解析的结果
     var users:[MyUser] = []
     static let mself=XMLUtils()
     //当前元素名
     var currentElement = ""
      
     //当前用户
     var user:MyUser!
     var tel:MyUser.Tel!
    
    
    
    
    // 遇到一个开始标签时调用
    func parser(_ parser: XMLParser, didStartElement elementName: String,
                   namespaceURI: String?, qualifiedName qName: String?,
                   attributes attributeDict: [String : String] = [:]) {
           currentElement = elementName

        switch elementName {
            case "User":
                //创建一个新用户对象
                user = MyUser()
                //保存下id
                user.id = attributeDict["id"]
            case "tel":
                tel = MyUser.Tel()
            default:
              break
            
       }
    }
    
    
        
     // 遇到字符串时调用
     func parser(_ parser: XMLParser, foundCharacters string: String) {
           let data = string.trimmingCharacters(in: .whitespacesAndNewlines)
           //接下来每遇到一个字符,将该字符追加到相应的 property 中
           switch currentElement{
           case "name":
               user.name = user.name ?? "" + data
           case "mobile":
               tel.mobile = tel.mobile ?? "" +  data
           case "home":
               tel.home = tel.home ?? "" + data
           default:
               break
           }
       }
    
    
        
    // 遇到结束标签时调用
    func parser(_ parser: XMLParser, didEndElement elementName: String,
                   namespaceURI: String?, qualifiedName qName: String?) {
           //标签User结束时将该用户对象,存入数组容器。
        switch elementName {
        case "User":
            users.append(user)
        case "tel":
            user.tel=tel
        default:
            break
        }
        
        
       
    }

    func parserDidEndDocument(_ parser: XMLParser) {
        //输出结果
        for i in 0...(users.count - 1 ){
            if let tel = users[i].tel  {
                if let mobile = tel.mobile {
                print("User: id:\(users[i].id!),name:\(users[i].name!),"
                        + "mobile:\(mobile),"
                        + "home:\(tel.home)")
                }
            }
            
        }
        SwiftEventBus.post("personFetchEvent", sender:users[0])
    }
    //用户对象
  
}

四.调用展示

//
//  SwiftUI_XML_View.swift
//  swiftUiStudy
//
//  Created by 王飞 on 2020/12/2.
//

import SwiftUI
import SwiftEventBus
struct SwiftUI_XML_View: View{
    //记录当前节点的名字
    var currentNodeName:String!
    @State var xmlName:String! = "解析结果"
    
    var body: some View {
        
        Text("\(xmlName)").onTapGesture {
            let xmlUrl = URL(fileURLWithPath: Bundle.main.path(forResource: "users", ofType: "xml")!)
            let parser = XMLParser(contentsOf:xmlUrl)
            print("解析开始")
            //设置delegate
            parser?.delegate=XMLUtils.mself
            //开始解析
            parser?.parse()
            
            SwiftEventBus.onMainThread(XMLUtils(), name:"personFetchEvent") { result in
                let user : MyUser = result!.object! as! MyUser
                print(user.name) // will output "john doe"
                xmlName=user.name
            }
           
        }
    }
    
    
 
}

struct SwiftUI_XML_View_Previews: PreviewProvider{
    static var previews: some View {
        SwiftUI_XML_View()
    }
}