How can I keep accents in xml file after editing it?

Viewed 451

I'm trying to modify one attritube value in a xml config file, but when I save it, accent is replaced : ô => ô

Here is my code

    $xmlDoc = [XML](Get-Content "C:\MesInterface.config")
    $xmlDoc.configuration.ContrôleFlan.lastId = "0"
    $xmlDoc.Save("C:\MesInterface.config")

And what is happening after save

<?xml version="1.0" encoding="utf-8"?>
<configuration>
<ContrôleFlan lastId="0"/></configuration>
3 Answers

The general advice is to save the .ps1 file encoded as UTF-8 with BOM when international characters are involved.

Powershell v5.1 and earlier assume .ps1 files without a BOM (byte-order mark) to be encoded in the Windows-1252 codepage. From the doc page:

PowerShell 5.1 and below default to Windows-1252 encoding when there's no BOM.
For interoperability reasons, it's best to save scripts in a Unicode format with a BOM.

[ EDIT ] The official documentation quoted above appears to be wrong, as pointed out by @mklement0 in a comment (and reported to MS). PowerShell defaults to the system-wide default ANSI codepage, instead, often but not necessarily Windows-1252. [ end EDIT ]

The OP wrote in a comment:

[the .ps1 file] was already UTF8 encoded, I've switched to ANSI and now my XML is correct

This worked in this case because the character ô happens to exist in the Windows-1252 (ANSI) codepage.

However, saving the .ps1 file as ANSI will still cause it to fail for characters outside the Windows-1252 codepage. For example, the following will fail if copy/pasted to a .ps1 file saved with ANSI encoding, but will work if saved as UTF-8 with BOM.

    $xmlDoc = [XML](Get-Content "C:\MesInterface.config")
    $xmlDoc.configuration.ContrôleFlan.lastId = "αß©∂€"
    $xmlDoc.Save("C:\MesInterface.config")

There are two ultimately independent aspects to consider:

  • If your script file itself contains (code-relevant) non-ASCII characters such as ô, you must ensure that the PowerShell engine interprets them correctly.

    • The safest bet is to save your *.ps1 files using UTF-8 encoding with a BOM (UTF-16 with a BOM is also an option, but wasteful for files that primarily contain ASCII-range characters): it ensures that both PowerShell editions (Windows PowerShell and PowerShell [Core] v6+) read the file correctly and that all Unicode characters can be represented.
  • Since your XML document has an XML declaration that specifies its encoding - encoding="utf-8" - you should defer to .NET's XML API to recognize the encoding both when the document is read from a file and when it is written back to a file.

    • By using the .Save() method you're already deferring to the API on writing the document back to the file, but the use of Get-Content - which knows nothing about XML - for reading means that the file can get misinterpreted; specifically, in the absence of a UTF-8 BOM, Windows PowerShell will read the file as ANSI-encoded (but note that PowerShell [Core] v6+ now sensibly defaults to UTF-8).

    • Therefore, instead of Get-Content, use the [xml] type's .Load() method to read the file, which respects the encoding specified in the XML declaration:

$file = 'C:\MesInterface.config'

$xmlDoc = New-Object xml

$xmlDoc.Load($file)

$xmlDoc.configuration.ContrôleFlan.lastId = "0"

$xmlDoc.Save($file)

If you want to set the encoding in code for your XML you could do something like this:

$settings = New-Object System.Xml.XmlWriterSettings
$settings.Encoding = [System.Text.Encoding]::Unicode

$writer = [System.Xml.XmlWriter]::Create("c:\test\mydata.xml", $settings);

$xmlDoc.Save($writer)

$writer.Close()
Related