处理XML/plist非法字符时的节点删除与格式保留问题求助
解决Embarcadero iOS plist模板的PowerShell编辑问题
背景
Embarcadero IDE通过plist模板构建iOS应用,这类XML模板包含<%VersionInfoPListKeys%>这类仅在Embarcadero部署阶段解析的占位符。由于IDE不支持Xcode的$(PRODUCT_BUNDLE_IDENTIFIER)变量,必须通过PowerShell脚本手动编辑模板,添加关联Bundle ID的自定义条目。
问题现象
这些占位符属于XML非法内容,导致PowerShell无法直接将文件转换为XML对象。尝试先将<%替换为<_、%>替换为_/>,处理完成后再替换回原符号,但执行脚本后出现两个问题:
- 新增的array节点无格式化,以单行形式存在;
- 无法删除已存在的对应array节点,仅能删除key节点,导致内容重复。
若使用Get-Content加载文件,节点增删逻辑正常,但PreserveWhitespace属性失效,输出内容为单行。
现有脚本代码
加载plist文件
$plist_raw = ([System.IO.File]::ReadAllText($plistPath)) -replace "<%", "<_" -replace "%>", "_/>" $plist = [xml]::new() $plist.PreserveWhitespace = $true $plist.LoadXml($plist_raw)
添加/替换array节点
$arrayKey = "some_key" $keyNode = (Select-Xml -Xml $plist -XPath "//dict/key" | Where-Object { $_.Node.InnerText -eq $arrayKey } | Select-Object -First 1).Node if ($keyNode) { $plist.plist.dict.RemoveChild($keyNode.NextSibling) $plist.plist.dict.RemoveChild($keyNode) } $arrayNode = $plist.CreateElement('array') $arrayKeyNode = $plist.CreateElement('key') $arrayKeyNode.InnerText = $arrayKey $plist.plist.dict.AppendChild($arrayKeyNode) $plist.plist.dict.AppendChild($arrayNode) $value1 = "$bundleId.foo" $value2 = "$bundleId.bar" foreach ($item in @($value1, $value2)) { $newItem = $plist.CreateElement('string') $newItem.InnerText = $item $arrayNode.AppendChild($newItem) Write-Host "Identifier '$item' has been added to the array '$arrayKey'." }
保存plist文件
($plist.OuterXml -replace "<_", "<%" -replace "_/>", "%>" -replace "_ />", "%>") | Set-Content $plistPath
预期与实际输出
- 预期:
some_key对应的array节点格式正确,旧节点被完全覆盖; - 实际:旧array节点未被删除,新节点无格式化,内容重复。
解决方案
1. 修复旧节点删除逻辑
原逻辑中keyNode.NextSibling可能指向空白节点(换行、空格)而非目标array节点,需跳过空白节点定位到真正的array节点:
$arrayKey = "some_key" $keyNode = (Select-Xml -Xml $plist -XPath "//dict/key[text()='$arrayKey']").Node if ($keyNode) { $currentNode = $keyNode.NextSibling # 跳过空白节点,找到后续的array节点 while ($currentNode -and $currentNode.NodeType -eq [System.Xml.XmlNodeType]::Whitespace) { $currentNode = $currentNode.NextSibling } # 确认找到的是array节点再删除 if ($currentNode -and $currentNode.Name -eq 'array') { $plist.plist.dict.RemoveChild($currentNode) } $plist.plist.dict.RemoveChild($keyNode) }
2. 解决XML格式化问题
OuterXml输出无格式XML,改用XmlWriter生成带缩进的格式化内容,匹配plist默认格式:
# 配置XML写入设置 $xmlSettings = New-Object System.Xml.XmlWriterSettings $xmlSettings.Indent = $true $xmlSettings.IndentChars = "`t" # 用制表符对齐 $xmlSettings.OmitXmlDeclaration = $false $xmlSettings.NewLineChars = "`n" # 统一换行符 # 写入内存流生成格式化XML $memoryStream = New-Object System.IO.MemoryStream $writer = [System.Xml.XmlWriter]::Create($memoryStream, $xmlSettings) $plist.WriteTo($writer) $writer.Flush() $memoryStream.Position = 0 # 读取格式化内容并替换占位符 $reader = New-Object System.IO.StreamReader($memoryStream) $formattedXml = $reader.ReadToEnd() $reader.Close() $writer.Close() $memoryStream.Close() # 保存最终内容 $finalContent = $formattedXml -replace "<_", "<%" -replace "_/>", "%>" Set-Content -Path $plistPath -Value $finalContent -Encoding UTF8
3. 优化文件加载逻辑
显式指定UTF8编码加载plist文件,避免编码问题:
$plist_raw = ([System.IO.File]::ReadAllText($plistPath, [System.Text.Encoding]::UTF8)) -replace "<%", "<_" -replace "%>", "_/>" $plist = [xml]::new() $plist.PreserveWhitespace = $true $plist.LoadXml($plist_raw)
内容的提问来源于stack exchange,提问作者Marvin Zakrzewski
相关产品推荐
相关产品推荐

