You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何解析复杂XML结构并填充Swift Program结构体?

解析嵌套同名XML标签的解决方案

处理Swift XMLParser中同名标签的核心是用状态栈跟踪解析上下文,结合元素属性区分不同场景下的同名节点。下面是针对你的CzechTV项目的具体实现方案:

1. 定义解析状态枚举

先枚举所有需要跟踪的解析节点,明确每个节点的上下文:

enum ParserState {
    case rootProgram  // 带属性的根节点<program>
    case porad        // <porad>节点
    case linkedProgram // 内部不带属性的<program>链接节点
    // 可根据你的XML结构扩展其他状态
}

2. 在Parser类中维护状态栈与临时对象

在你的Parser类里添加以下属性,用于跟踪当前解析状态和正在构建的结构体实例:

class ProgramParser: NSObject, XMLParserDelegate {
    private var stateStack: [ParserState] = []
    private var currentText = ""
    
    // 最终解析结果
    var rootProgram: Program?
    // 临时对象,用于构建过程中存储数据
    private var currentPorad: Porad?
    private var currentLinkedProgram: Program?
    
    // ... 已有的网络获取等代码
}

3. 处理元素开始事件(didStartElement)

在这个方法里,通过元素属性+当前栈顶状态区分同名标签:

func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
    currentText = "" // 重置当前文本
    
    switch elementName {
    case "program":
        // 判断是否为根节点:根节点带有特定属性(比如date、id等)
        if let _ = attributeDict["date"] { // 假设根节点有date属性
            stateStack.append(.rootProgram)
            rootProgram = Program()
            // 给根节点Program赋值属性
            rootProgram?.date = attributeDict["date"]
        } else {
            // 结合当前栈状态判断:如果当前在porad节点内,就是链接的program
            if stateStack.last == .porad {
                stateStack.append(.linkedProgram)
                currentLinkedProgram = Program()
            }
        }
    case "porad":
        stateStack.append(.porad)
        currentPorad = Porad()
        // 给Porad赋值属性(如果有的话)
        currentPorad?.title = attributeDict["title"]
    // 处理其他元素...
    default:
        break
    }
}

4. 处理字符内容(foundCharacters)

根据当前栈顶的状态,将字符内容赋值给对应对象的属性:

func parser(_ parser: XMLParser, foundCharacters string: String) {
    currentText += string.trimmingCharacters(in: .whitespacesAndNewlines)
}

5. 处理元素结束事件(didEndElement)

在元素结束时,将临时对象挂载到父对象,并弹出当前状态:

func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
    guard let currentState = stateStack.last else { return }
    
    switch (elementName, currentState) {
    case ("program", .rootProgram):
        stateStack.removeLast()
    case ("program", .linkedProgram):
        // 将链接的Program添加到当前Porad的属性中
        currentPorad?.linkedProgram = currentLinkedProgram
        currentLinkedProgram = nil
        stateStack.removeLast()
    case ("porad", .porad):
        // 将Porad添加到根Program的数组中
        rootProgram?.porads.append(currentPorad!)
        currentPorad = nil
        stateStack.removeLast()
    // 处理其他元素的内容赋值,比如Porad的描述等
    case ("description", .porad):
        currentPorad?.description = currentText
    default:
        break
    }
}

关键注意点

  • 状态栈的核心作用:通过栈的层级记录当前解析的上下文,避免同名标签混淆
  • 属性区分逻辑:根节点和内部的核心差异是属性,一定要利用attributeDict做判断
  • 字符拼接:foundCharacters可能会多次调用,必须用currentText累积内容,避免丢失部分文本
  • 对象生命周期:临时对象(如currentPorad)要在元素结束后及时挂载到父对象,并重置为nil,避免内存问题

内容的提问来源于stack exchange,提问作者Marcel M.

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 00:48:20