Powershell XMLDocument salva come UTF-8 senza BOM

Aug 19 2020

Ho costruito un oggetto XML di tipo System.Xml.XmlDocument.

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

Uso il metodo save () per salvarlo in un file.

$scheme.save()

Questo salva il file in formato UTF-8 con BOM. La distinta componenti causa problemi con altri script su tutta la linea.

Quando apriamo il file XML in Notepad ++ e lo salviamo come UTF-8 (senza BOM), gli altri script su tutta la linea non hanno problemi. Quindi mi è stato chiesto di salvare lo script senza la distinta componenti.

La documentazione MS per il metodo di salvataggio afferma:

Il valore dell'attributo di codifica viene preso dalla proprietà XmlDeclaration.Encoding. Se XmlDocument non dispone di XmlDeclaration o se XmlDeclaration non dispone di un attributo di codifica, neanche il documento salvato ne avrà uno.

La documentazione MS su XmlDeclaration elenca le proprietà di codifica di UTF-8, UTF-16 e altri. Non menziona un BOM.

La XmlDeclaration ha una proprietà di codifica che esclude la BOM?

PS. Questo comportamento è identico in Powershell 5 e Powershell 7.

Risposte

2 mklement0 Aug 19 2020 at 09:39

Sfortunatamente, la presenza esplicita di un encoding="utf-8"attributo nella dichiarazione di un documento XML fa sì che .NET .Save()sul documento diventi un file con codifica UTF-8 con BOM se viene fornito un percorso del file di destinazione, il che può effettivamente causare problemi.

Una richiesta di modifica è stata respinta per paura di rompere la compatibilità con le versioni precedenti; ecco una richiesta per documentare almeno il comportamento.

Un po 'ironicamente, l' assenza di un encodingattributo causa la .Save()creazione di file con codifica UTF-8 senza BOM.

Una soluzione semplice è quindi rimuovere l'attributo di codifica [1] ; per esempio:

# 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] Questo è sicuro, perché la raccomandazione XML W3C impone di fatto UTF-8 come impostazione predefinita in assenza sia di una distinta materiali che di un encodingattributo.

1 MathiasR.Jessen Aug 19 2020 at 05:20

Come spiega BACON nei commenti , il valore stringa Encodingdell'attributo nella dichiarazione XML non ha alcuna relazione con il modo in cui il file contenente il documento è codificato.

È possibile controllare questo creando sia un StreamWritero un XmlWritercon un non-BOM UTF8Encoding, quindi passare che per 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()

In alternativa usa un [XmlWriter]:

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

Il secondo argomento è un [XmlWriterSettings]oggetto, attraverso il quale possiamo esercitare un maggiore controllo sulle opzioni di formattazione oltre a impostare esplicitamente la codifica:

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