Powershell XMLDocument enregistrer au format UTF-8 sans nomenclature

Aug 19 2020

J'ai construit un objet XML de type System.Xml.XmlDocument.

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

J'utilise la méthode save () pour l'enregistrer dans un fichier.

$scheme.save()

Cela enregistre le fichier au format UTF-8 avec BOM. La nomenclature provoque des problèmes avec d'autres scripts sur toute la ligne.

Lorsque nous ouvrons le fichier XML dans Notepad ++ et que nous l'enregistrons au format UTF-8 (sans la nomenclature), les autres scripts sur la ligne n'ont pas de problème. On m'a donc demandé de sauvegarder le script sans la nomenclature.

La documentation MS pour la méthode de sauvegarde indique:

La valeur de l'attribut de codage provient de la propriété XmlDeclaration.Encoding. Si XmlDocument n'a pas de XmlDeclaration, ou si XmlDeclaration n'a pas d'attribut de codage, le document enregistré n'en aura pas non plus.

La documentation MS sur XmlDeclaration répertorie les propriétés de codage de UTF-8, UTF-16 et autres. Il ne mentionne pas de nomenclature.

Le XmlDeclaration a-t-il une propriété de codage qui exclut la nomenclature?

PS. Ce comportement est identique dans Powershell 5 et Powershell 7.

Réponses

2 mklement0 Aug 19 2020 at 09:39

Malheureusement, la présence explicite d'un encoding="utf-8"attribut dans la déclaration d'un document XML entraîne .NET .Save()du document vers un fichier encodé en UTF-8 avec BOM si un chemin de fichier cible est donné, ce qui peut en effet causer des problèmes.

Une demande de modification a été rejetée par crainte de briser la compatibilité ascendante; voici une demande pour au moins documenter le comportement.

Ironiquement, l' absence d' encodingattribut provoque la .Save()création de fichiers encodés en UTF-8 sans nomenclature.

Une solution simple est donc de supprimer l'attribut encoding [1] ; par exemple:

# 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] C'est sûr à faire, car la recommandation XML W3C impose effectivement UTF-8 comme valeur par défaut en l'absence à la fois d'une nomenclature et d'un encodingattribut.

1 MathiasR.Jessen Aug 19 2020 at 05:20

Comme l' explique BACON dans les commentaires , la valeur de chaîne de l' Encodingattribut dans la déclaration XML n'a aucune incidence sur la façon dont le fichier contenant le document est codé.

Vous pouvez contrôler ce soit en créant un StreamWriterou XmlWriteravec un non-nomenclature UTF8Encoding, puis passer que pour 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()

Vous pouvez également utiliser un [XmlWriter]:

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

Le deuxième argument est un [XmlWriterSettings]objet, à travers lequel nous pouvons exercer un plus grand contrôle sur les options de formatage en plus de définir explicitement l'encodage:

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