Powershell XMLDocument zapisuje jako UTF-8 bez BOM
Zbudowałem obiekt XML typu System.Xml.XmlDocument.
$scheme.gettype()
IsPublic IsSerial Name BaseType
-------- -------- ---- --------
True False XmlDocument System.Xml.XmlNode
Używam metody save (), aby zapisać go do pliku.
$scheme.save()
Spowoduje to zapisanie pliku w formacie UTF-8 z BOM. Zestawienie komponentów powoduje problemy z innymi skryptami w przyszłości.
Kiedy otwieramy plik XML w Notepad ++ i zapisujemy go jako UTF-8 (bez BOM), inne skrypty w dół linii nie mają problemu. Więc zostałem poproszony o zapisanie skryptu bez BOM.
Dokumentacja MS dotycząca metody zapisu podaje:
Wartość atrybutu kodowania jest pobierana z właściwości XmlDeclaration.Encoding. Jeśli XmlDocument nie ma XmlDeclaration lub jeśli XmlDeclaration nie ma atrybutu kodowania, zapisany dokument też nie będzie miał.
Dokumentacja MS dotycząca XmlDeclaration zawiera listę właściwości kodowania UTF-8, UTF-16 i innych. Nie wspomina o BOM.
Czy XmlDeclaration ma właściwość kodowania, która pomija BOM?
PS. To zachowanie jest identyczne w programach Powershell 5 i Powershell 7.
Odpowiedzi
Niestety, jawna obecność encoding="utf-8"atrybutu w deklaracji dokumentu XML powoduje, że .NET przesyła dokument do .Save()pliku zakodowanego w UTF-8 z BOM, jeśli podana jest ścieżka do pliku docelowego, co może rzeczywiście powodować problemy.
Prośba o zmianę tego została odrzucona z obawy o zerwanie wstecznej kompatybilności; oto prośba, aby przynajmniej udokumentować zachowanie.
Nieco Paradoksalnie, brak o encodingatrybut powoduje .Save()tworzyć UTF-8 zakodowanych plików bez LM.
Dlatego prostym rozwiązaniem jest usunięcie atrybutu kodowania [1] ; na przykład:
# 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] Jest to bezpieczne, ponieważ zalecenie XML W3C skutecznie narzuca UTF-8 jako domyślny w przypadku braku zarówno BOM, jak i encodingatrybutu.
Jak wyjaśnia BACON w komentarzach , wartość ciągu Encodingatrybutu w deklaracji XML nie ma żadnego wpływu na sposób kodowania pliku zawierającego dokument.
Można to kontrolować poprzez tworzenie albo StreamWriteralbo XmlWriterz braku BOM UTF8Encoding, a następnie przekazać to do 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()
Alternatywnie użyj [XmlWriter]:
# XmlWriter Example
$writer = [System.Xml.XmlWriter]::Create($filename, @{ Encoding = $encoding })
Drugi argument to [XmlWriterSettings]obiekt, dzięki któremu możemy sprawować większą kontrolę nad opcjami formatowania oprócz jawnego ustawienia kodowania:
$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>