替换XML/plist文件中的非法字符并保持格式

问题描述 投票:0回答:1

Embarcadero IDE 使用模板 plist 文件来构建 iOS 应用程序。 模板文件是一个简单的 XML 文件,但它包含 Embarcadero 在部署期间解析的非法占位符符号。 (例如

\<%VersionInfoPListKeys%\>

此外,由于 IDE 的限制,我必须手动编辑模板文件,以添加引用包 ID 的自定义条目。 由于 Embarcadero 不支持 plist 文件中的 xcode 变量(例如

$(PRODUCT_BUNDLE_IDENTIFIER)
),因此我必须使用 powershell 脚本手动编辑该文件。 但是,由于这些占位符符号,powershell 无法将文件的内容转换为 XML 对象。

因此我尝试将那些 % 字符替换为有效符号,并在保存文件之前将原始符号替换回去。

我尝试了以下操作:($plistPath 和 $bundleId 是启动参数)

加载到plist中:

$plist_raw = ([System.IO.File]::ReadAllText($plistPath)) -replace "<%", "<_" -replace "%>", "_/>"
$plist = [xml]::new()
$plist.PreserveWhitespace = $true
$plist.LoadXml($plist_raw)

向 plist 添加/替换数组节点:

$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

问题是,如果我这样做,数组节点将作为单行附加到文件中并且没有格式。它也无法删除现有的数组节点。

输入文件:“info.plist.TemplateiOS.xml”

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"[]>
<plist version="1.0">
<dict>
<%VersionInfoPListKeys%>
<%ExtraInfoPListKeys%>
<%StoryboardInfoPListKey%>
<key>UIBackgroundModes</key>
<array>
  <string>fetch</string>
  <string>processing</string>
</array>
</dict>
</plist>

我期望的输出:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"[]>
<plist version="1.0">
<dict>
<%VersionInfoPListKeys%>
<%ExtraInfoPListKeys%>
<%StoryboardInfoPListKey%>
<key>UIBackgroundModes</key>
<array>
  <string>fetch</string>
  <string>processing</string>
</array>
<key>some_key</key>
<array>
  <string>some_bundleId.foo</string>
  <string>some_bundleId.bar</string>
</array>
</dict>
</plist>

我得到的输出:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"[]>
<plist version="1.0">
<dict>
<%VersionInfoPListKeys%>
<%ExtraInfoPListKeys%>
<%StoryboardInfoPListKey%>
<key>UIBackgroundModes</key>
<array>
  <string>fetch</string>
  <string>processing</string>
</array>
<array>
  <string>some_bundleId.foo</string>
  <string>some_bundleId.bar</string>
</array>
<key>some_key</key><array><string>some_bundleId.foo</string><string>some_bundleId.bar</string></array></dict>
</plist>

注意它只删除了键而不是数组,然后将新节点放在其下面。

如果我使用

Get-Content
加载 plist,它会正确附加/删除节点,但
PreserveWhitespace
属性不起作用,输出会写入单行。

为了澄清,上面的脚本应该添加一个键“some_key”和一个包含 2 个字符串的数组。如果该密钥已经存在,则应覆盖它。下面显示的第二个输出是当该键已经存在并且无法删除数组时的输出。使用 Get-Content 时它确实有效,因此它一定是缺少格式的副作用。

xml powershell plist pretty-print
1个回答
0
投票

将非法模板节点重写为有效的 XML 注释,追加新节点,然后将文档转换回文本并再次删除注释。

首先将无效的节点数据变成注释:

# read plist contents into memory
$plistRawText = Get-Content $plistPath -Raw 

# comment out the macro expressions and cast to xml doc
$plist = [xml]($plistRawText -replace '<%[^%]+%>','<!--$0-->')

现在我们需要将新数据添加到列表中。假设

<key>
/
<array>
节点成对出现,我建议将您的数据“捆绑”在一起,如下所示:

# define data to add to list
$dataToAdd = [ordered]@{
    "some_key" = @('some_bundleId.foo', 'some_bundleId.bar')
    # "another_key" = @('some_other_value')
}

# locate parent element
$dictElement = $plist.SelectSingleNode('//plist/dict')

# iterate over the new data entries
foreach ($entry in $dataToAdd.GetEnumerator()) {
    # create and add <key> node
    $keyElement = $plist.CreateElement('key')
    $keyElement.InnerText = $entry.Key
    $null = $dictElement.AppendChild($keyElement)

    # create and add <array> node
    $arrayElement = $plist.CreateElement('array')
    foreach ($value in $entry.Value) {
        $stringElement = $plist.CreateElement('string')
        $stringElement.InnerText = $value
        $null = $arrayElement.AppendChild($stringElement)
    }
    $null = $dictElement.AppendChild($arrayElement)
}

最后,我们需要将 xml 文档对象转回文本,但将其保留在内存中,以便我们可以删除之前添加的注释。为此,我们可以使用

[XmlWriter]
来控制文档的格式:

# create an XmlWriter with appropriate formatting settings
$result = [System.Text.StringBuilder]::new()
$settings = [System.Xml.XmlWriterSettings]@{
    Indent = $true
    IndentChars = '  '
    NewLineChars = "`r`n"
    NewLineHandling = 'Replace'
}
$xmlWriter = [System.Xml.XmlWriter]::Create($result, $settings)

# save document to writer, grab resulting text
try {
    $plist.Save($xmlWriter)
    $plistModifiedText = $result.ToString()
}
finally {
    # clean up
    if ($xmlWriter -is [IDisposable]){
        $xmlWriter.Dispose()
    }
    $result.Clear()
}

最后删除内容就完成了:

# remove comments
$plistModifiedText -replace '<!--(<%[^%]+%>)-->', '$1'
© www.soinside.com 2019 - 2024. All rights reserved.