如何解析复杂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.
相关产品推荐
相关产品推荐

