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

Swift实现XML压缩:移除换行与行前空白,保留节点内部空格

在Swift中实现XML压缩(保留节点内部空格)

嘿,这个问题我之前处理XML的时候也踩过坑!直接用replacingOccurrences(of:)替换空格或者换行肯定不行,一不小心就会把节点内容里的有效空格(比如<Value1>company name</Value1>里的空格)给删掉。这里有两个实用的方案,你可以根据自己的需求选:

方案一:正则表达式快速处理

如果你的XML结构比较简单,没有CDATA、注释这类特殊内容,正则表达式是最快的实现方式。核心思路是只删除标签之间的格式化空白,而保留节点内容里的空格。

代码示例

func compressXML(_ xmlString: String) -> String {
    // 匹配 ">" 后接任意空白字符(换行、制表符、空格)再到 "<" 的部分
    let pattern = "(>\\s+<)"
    guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else {
        return xmlString
    }
    let range = NSRange(xmlString.startIndex..., in: xmlString)
    // 把匹配到的空白部分替换成 "><",直接连接标签
    let compressed = regex.stringByReplacingMatches(in: xmlString, range: range, withTemplate: "><")
    
    // 额外处理XML声明后的空白(比如<?xml...?>后面的换行)
    let cleanedDeclaration = compressed.replacingOccurrences(of: "\\?>\\s+<", with: "?>", options: .regularExpression)
    return cleanedDeclaration
}

// 使用示例
let originalXML = """
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<RootNode attribute1="1.0" attribute2="client" attribute3="apiKey">
    <Node>
        <SubNode>
            <Value1>company name</Value1>
            <Value2>userName</Value2>
            <Value3>password</Value3>
        </SubNode>
    </Node>
    <OtherNode attribute="datatype" attribute="method"/>
</RootNode>
"""

let compressedXML = compressXML(originalXML)
print(compressedXML)
// 输出:<?xml version="1.0" encoding="UTF-8" standalone="yes"?><RootNode attribute1="1.0" attribute2="client" attribute3="apiKey"><Node><SubNode><Value1>company name</Value1><Value2>userName</Value2><Value3>password</Value3></SubNode></Node><OtherNode attribute="datatype" attribute="method"/></RootNode>

方案二:XML解析器实现(更健壮)

如果你的XML包含CDATA、注释,或者结构复杂到正则容易出错的程度,用Foundation自带的XMLParser是最稳妥的选择。解析器会准确区分标签、属性、内容的边界,完全不用担心误删有效内容。

代码示例

import Foundation

class XMLCompressor: NSObject, XMLParserDelegate {
    private var resultString = ""
    private var currentElementContent = ""
    
    func compress(xmlString: String) -> String? {
        resultString = ""
        currentElementContent = ""
        guard let data = xmlString.data(using: .utf8) else { return nil }
        
        let parser = XMLParser(data: data)
        parser.delegate = self
        
        guard parser.parse() else {
            print("XML解析失败:\(parser.parserError?.localizedDescription ?? "未知错误")")
            return nil
        }
        return resultString
    }
    
    // 处理XML声明(比如<?xml...?>)
    func parser(_ parser: XMLParser, foundProcessingInstructionWithTarget target: String, data: String?) {
        resultString += "<?\(target) \(data ?? "")?>"
    }
    
    // 开始解析标签
    func parser(_ parser: XMLParser, didStartElement elementName: String, namespaceURI: String?, qualifiedName qName: String?, attributes attributeDict: [String : String] = [:]) {
        // 先把之前收集的元素内容(如果有)添加到结果,只去掉前后的格式化空白
        if !currentElementContent.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
            resultString += currentElementContent.trimmingCharacters(in: .whitespacesAndNewlines)
            currentElementContent = ""
        }
        
        // 拼接开始标签和属性
        var startTag = "<\(elementName)"
        for (key, value) in attributeDict {
            startTag += " \(key)=\"\(value)\""
        }
        startTag += ">"
        resultString += startTag
    }
    
    // 收集元素内部的内容
    func parser(_ parser: XMLParser, foundCharacters string: String) {
        currentElementContent += string
    }
    
    // 结束解析标签
    func parser(_ parser: XMLParser, didEndElement elementName: String, namespaceURI: String?, qualifiedName qName: String?) {
        // 处理元素内容
        if !currentElementContent.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty {
            resultString += currentElementContent.trimmingCharacters(in: .whitespacesAndNewlines)
            currentElementContent = ""
        }
        // 添加结束标签
        resultString += "</\(elementName)>"
    }
    
    // 可选:处理CDATA块,原封不动保留内容
    func parser(_ parser: XMLParser, foundCDATA CDATABlock: Data) {
        if let cdataString = String(data: CDATABlock, encoding: .utf8) {
            resultString += "<![CDATA[\(cdataString)]]>"
        }
    }
}

// 使用示例
let compressor = XMLCompressor()
if let compressedXML = compressor.compress(xmlString: originalXML) {
    print(compressedXML)
}

方案对比

  • 正则方案:代码简洁,执行速度快,但仅适用于结构简单的XML,无法处理CDATA、注释等特殊情况。
  • 解析器方案:完全兼容所有合法XML,能正确处理CDATA、注释、特殊字符,代码量稍大但稳定性拉满。

内容的提问来源于stack exchange,提问作者Vladimir

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.15 04:57:58