Powershell XMLDocument保存为无BOM的UTF-8

Bag*_*era 4 .net powershell byte-order-mark utf-8

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

$scheme.gettype()
IsPublic IsSerial Name BaseType                                                         
-------- -------- ---- --------                                                         
True     False    XmlDocument System.Xml.XmlNode 
Run Code Online (Sandbox Code Playgroud)

我使用 save() 方法将其保存到文件中。

$scheme.save()
Run Code Online (Sandbox Code Playgroud)

这将以带有 BOM 的 UTF-8 格式保存文件。BOM 会导致其他脚本出现问题。

当我们在 Notepad++ 中打开 XML 文件并将其保存为 UTF-8(不含 BOM)时,其他脚本不会出现问题。所以我被要求保存没有 BOM 的脚本。

保存方法的 MS 文档指出:

编码属性的值取自 XmlDeclaration.Encoding 属性。如果 XmlDocument 没有 XmlDeclaration,或者 XmlDeclaration 没有编码属性,则保存的文档也不会具有编码属性。

XmlDeclaration 上的 MS 文档列出了 UTF-8、UTF-16 等的编码属性。它没有提到 BOM。

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

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

Mat*_*sen 5

正如BACON 在注释中所解释的那样,Encoding包含文档的文件的编码方式没有任何关系。

您可以通过创建 a或带有非 BOM 的StreamWriteran来控制这一点,然后将传递给:XmlWriterUTF8EncodingSave($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()
Run Code Online (Sandbox Code Playgroud)

或者使用[XmlWriter]

# XmlWriter Example
$writer = [System.Xml.XmlWriter]::Create($filename, @{ Encoding = $encoding })
Run Code Online (Sandbox Code Playgroud)

第二个参数是一个[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>
Run Code Online (Sandbox Code Playgroud)


mkl*_*nt0 5

不幸的是,在 XML 文档声明中存在显式encoding="utf-8"属性[xml]会导致 .NET 的( System.Xml.XmlDocument).Save()文档类型在给定文件路径时变成带有BOM 的UTF-8 编码文件,这确实会导致问题(即使它不应该[1])。

原则上已批准更改此设置的请求,但从.NET 6.0 起尚未实现(由于有关更改[System.Text.Encoding]::UTF8使用 BOM 的大量讨论,在这种情况下.Save()也将自动不再创建 BOM)。

有点讽刺的是,缺少属性encoding导致.Save()创建没有BOM 的UTF-8 编码文件。

因此,一个简单的解决方案是删除编码属性[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")
Run Code Online (Sandbox Code Playgroud)

[1] 根据XML W3C 建议:“以 UTF-8 编码的实体可以以字节顺序标记开头”[BOM]。

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