Powershell XMLDocument salvo como UTF-8 sem BOM

Aug 19 2020

Criei um objeto XML do tipo System.Xml.XmlDocument.

$scheme.gettype()
IsPublic IsSerial Name BaseType                                                         
-------- -------- ---- --------                                                         
True     False    XmlDocument System.Xml.XmlNode 

Eu uso o método save () para salvá-lo em um arquivo.

$scheme.save()

Isso salva o arquivo no formato UTF-8 com BOM. O BOM causa problemas com outros scripts no futuro.

Quando abrimos o arquivo XML no Notepad ++ e o salvamos como UTF-8 (sem o BOM), outros scripts na linha não têm problemas. Então, fui solicitado a salvar o script sem o BOM.

A documentação da MS para os estados do método de salvamento :

O valor do atributo encoding é obtido da propriedade XmlDeclaration.Encoding. Se o XmlDocument não tiver um XmlDeclaration ou se o XmlDeclaration não tiver um atributo de codificação, o documento salvo também não terá um.

A documentação da MS sobre XmlDeclaration lista propriedades de codificação de UTF-8, UTF-16 e outros. Não menciona um BOM.

O XmlDeclaration tem uma propriedade de codificação que exclui o BOM?

PS. Esse comportamento é idêntico no Powershell 5 e no Powershell 7.

Respostas

2 mklement0 Aug 19 2020 at 09:39

Infelizmente, a presença explícita de um encoding="utf-8"atributo na declaração de um documento XML transforma o .NET .Save()no documento em um arquivo codificado em UTF-8 com BOM se um caminho de arquivo de destino for fornecido, o que pode de fato causar problemas.

Um pedido para mudar isso foi rejeitado por medo de quebrar a compatibilidade com versões anteriores; aqui está um pedido para pelo menos documentar o comportamento.

Ironicamente, a ausência de um encodingatributo causa .Save()a criação de arquivos codificados em UTF-8 sem um BOM.

Uma solução simples é, portanto, remover o atributo de codificação [1] ; por exemplo:

# 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] Isso é seguro, porque a recomendação W3C do XML efetivamente determina que o UTF-8 seja o padrão na ausência de um BOM e de um encodingatributo.

1 MathiasR.Jessen Aug 19 2020 at 05:20

Como o BACON explica nos comentários , o valor da string do Encodingatributo na declaração XML não tem qualquer relação com a forma como o arquivo que contém o documento é codificado.

Você pode controlar isso criando tanto um StreamWriterou uma XmlWritercom um não-BOM UTF8Encoding, em seguida, passar que para 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()

Como alternativa, use um [XmlWriter]:

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

O segundo argumento é um [XmlWriterSettings]objeto, por meio do qual podemos exercer maior controle sobre as opções de formatação, além de definir explicitamente a codificação:

$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>