【问题标题】:Powershell XMLDocument save as UTF-8 without BOMPowershell XMLDocument 保存为没有 BOM 的 UTF-8
【发布时间】:2020-12-08 02:21:29
【问题描述】:

我构建了一个 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 documentation for the save method 声明:

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

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

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

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

【问题讨论】:

  • 你打电话给Save()的哪个重载?问题的后半部分涉及<?xml 声明的encoding 属性,但是通过在Notepad++ 中重新保存文件来解决问题的部分表明真正的问题是文件本身的文本编码。为此,您可以使用$encoding = New-Object -TypeName 'System.Text.UTF8Encoding' -ArgumentList $false; $writer = New-Object -TypeName 'System.IO.StreamWriter' -ArgumentList $outputPath, $shouldAppend, $encoding 创建一个UTF-8 非BOM StreamWriter 并将其传递给Save()

标签: .net powershell utf-8 byte-order-mark


【解决方案1】:

作为BACON explains in the comments,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>

【讨论】:

    【解决方案2】:

    不幸的是,当给定一个文件路径带有 BOMUTF-8 编码文件,这确实会导致问题(即使它不应该[1])。

    A request to change this原则上被批准,但尚未在 .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] 这样做是安全的,因为在没有 BOM 和 encoding 属性的情况下,XML W3C Recommendation 有效地将 UTF-8 作为默认值。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-04-28
      • 1970-01-01
      • 2014-08-30
      • 2014-03-10
      • 2021-01-18
      • 1970-01-01
      相关资源
      最近更新 更多