XML needs 5 characters to be replaced, because they have special meaning in XML. These characters are <>&'"
Here, you can not simply prepend these characters with some escape character like the backslash, instead they need to be converted into named or numbered entities.
As you have noticed, this conversion happens automatically in [xml]
, but there are also a number of ways to do that manually if needed:
$str = "$<abc\bc>"
1) Using the String .Replace()
method:
$str.Replace("&", "&").Replace("'", "'").Replace('"', """).Replace("<", "<").Replace(">", ">")
2) Using .Net System.Security:
Add-Type -AssemblyName System.Security
[System.Security.SecurityElement]::Escape($str)
3) Using System.Xml.XmlDocument:
[xml]$doc = New-Object System.Xml.XmlDocument
$node = $doc.CreateElement("root")
$node.InnerText = $str
$node.InnerXml
4) Using Regex:
$str -ireplace '"|&#x(?:00)?22;|"', '"' `
-ireplace "'|&#x(?:00)?27;|'", ''' `
-ireplace '<|&#x(?:00)?3C;|<', '<' `
-ireplace '>|&#x(?:00)?3E;|>', '>' `
-ireplace '&|&#x(?:00)?26;|&(?!(?:[a-z]+|#[0-9]+|#x[0-9a-f]+);)', '&'
All of the above will convert the XML special characters into entities, so the result will look like this:
$<abc\bc>
If you absolutely don't want the XML special characters to be converted into entities, then there is AFAIK only the option to set the value as a CDATA section, which will wrap your string inside <![CDATA[
and ]]>
].
To use that in your example, change the line $xml.Deploy.app="$<abc\bc>"
into this:
$xml['Deploy']['app'].AppendChild($xml.CreateCDataSection("$<abc\bc>")) | Out-Null
Now it will print:
<?xml version="1.0"?>
<Deploy>
<app><![CDATA[$<abc\bc>]]></app>
</Deploy>
To read back this CDATA value as string, you can do either $value = $xml.Deploy.app.InnerText
or $value = $xml.Deploy.app.'#cdata-section'
Hope that explains