我构建了一个 System.Xml.XmlDocument 类型的 XML 对象。
$scheme.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False XmlDocument System.Xml.XmlNode
我使用 save() 方法将其保存到文件中。
$scheme.save()
这会将文件保存为带 BOM 的 UTF-8 格式。 BOM 会导致其他脚本出现问题。
当我们在 Notepad++ 中打开 XML 文件并将其保存为 UTF-8(不含 BOM)时,其他脚本不会出现问题。所以我被要求保存没有 BOM 的脚本。
保存方法的 MS 文档指出:
encoding 属性的值取自 XmlDeclaration.Encoding 属性。如果 XmlDocument 没有 XmlDeclaration,或者 XmlDeclaration 没有编码属性,则保存的文档也将没有编码属性。
有关 XmlDeclaration 的 MS 文档列出了 UTF-8、UTF-16 等的编码属性。它没有提到 BOM。
XmlDeclaration 是否具有省略 BOM 的编码属性?
PS。此行为在 Powershell 5 和 Powershell 7 中是相同的。
不幸的是,当给定文件路径
时,XML 文档声明中存在显式
encoding="utf-8"
属性会导致 .NET 的
[xml]
(System.Xml.XmlDocument
) 类型变为
.Save()
文档,到 UTF-8 编码的文件使用 BOM,这 确实会导致问题(尽管它不应该[1])。
更改此项的请求原则上已经绿灯,但自 .NET 9.0 起尚未实现(由于有关将
[System.Text.Encoding]::UTF8
更改为不使用 BOM 的更大讨论) ,在这种情况下 .Save()
也将自动不再创建 BOM)。
有点讽刺的是,encoding
属性的
缺失导致
.Save()
创建UTF-8编码的文件没有BOM。
因此,一个简单的解决方案是删除编码属性[2];例如:
# Create a sample XML document:
$xmlDoc = [xml] '<?xml version="1.0" encoding="utf-8"?><foo>bar</foo>'
# Remove the 'encoding' attribute from the declaration.
# Without this, the .Save() method below would create a UTF-8 file *with* BOM.
$xmlDoc.ChildNodes[0].Encoding = $null
# Now, saving produces a UTf-8 file *without* a BOM.
$xmlDoc.Save("$PWD/out.xml")
[1] 根据 XML W3C 建议:“以 UTF-8 编码的实体可以以字节顺序标记开头”[BOM]。
[2] 这样做是安全的,因为在缺乏 BOM 和 encoding
属性的情况下,
XML W3C 建议有效地强制将 UTF-8 作为默认值。
正如 BACON 在注释中解释的那样,XML 声明中
Encoding
属性的字符串值与包含文档的文件的编码方式没有任何关系。
您可以通过创建
StreamWriter
或带有非 BOM XmlWriter
的 UTF8Encoding
来控制这一点,然后将 that 传递给 Save($writer)
:
$filename = Resolve-Path path\to\output.xml
# Create UTF8Encoding instance, sans BOM
$encoding = [System.Text.UTF8Encoding]::new($false)
# Create StreamWriter instance
$writer = [System.IO.StreamWriter]::new($filename, $false, $encoding)
# Save using (either) writer
$scheme.Save($writer)
# Dispose of writer
$writer.Dispose()
或者使用
[XmlWriter]
:
# XmlWriter Example
$writer = [System.Xml.XmlWriter]::Create($filename, @{ Encoding = $encoding })
[XmlWriterSettings]
对象,通过它,除了显式设置编码之外,我们还可以对格式选项进行更大的控制:
$settings = [System.Xml.XmlWriterSettings]@{
Encoding = $encoding
Indent = $true
NewLineOnAttributes = $true
}
$writer = [System.Xml.XmlWriter]::Create($filename, $settings)
# <?xml version="1.0" encoding="utf-8"?>
# <Config>
# <Group
# name="PropertyGroup">
# <Property
# id="1"
# value="Foo" />
# <Property
# id="2"
# value="Bar"
# exclude="false" />
# </Group>
# </Config>