Powershell XMLDocument 保存为没有 BOM 的 UTF-8

Powershell XMLDocument save as UTF-8 without BOM

我构建了一个 XML 类型的对象 System.Xml.XmlDocument。

$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 documentation for the save method 状态:

The value of the encoding attribute is taken from the XmlDeclaration.Encoding property. If the XmlDocument does not have an XmlDeclaration, or if the XmlDeclaration does not have an encoding attribute, the saved document will not have one either.

MS documentation on XmlDeclaration 列出了 UTF-8、UTF-16 和其他编码属性。它没有提到 BOM。

XmlDeclaration 是否具有省略 BOM 的编码 属性?

PS。此行为在 Powershell 5 和 Powershell 7 中是相同的。

一样,XML 声明中 Encoding 属性的字符串值对包含文档的文件的编码方式没有任何影响。

您可以通过创建带有 non-BOM UTF8EncodingStreamWriterXmlWriter 来控制它,然后传递 thatSave($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>

不幸的是,在 XML 文档的声明中 显式 encoding="utf-8" 属性 的存在导致 .NET 的 [xml] (System.Xml.XmlDocument) 输入 .Save() 文档,当给定 文件路径 时,输入 UTF-8 编码文件 with BOM 确实会导致问题(尽管不应该[1 ]).

A request to change this 原则上 green-lighted,但 尚未在 .NET 6.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 Recommendation:“以 UTF-8 编码的实体可以以字节顺序标记开头”[BOM]。

[2] 这样做是安全的,因为 XML W3C Recommendation 在没有 BOM 和 encoding 属性的情况下有效地将 UTF-8 作为默认值。