I found out that xml.save ends multi line comments with LF on windows instead of CR LF. Why is that? Here is my code
[xml]$myXml= @"
<myTag>
<!-- multi line comment
each line ends with CR LF
in my code, but with LF
after .save gets called
end of my multi line comment -->
</myTag>
"@
$myXml.save("C:\temp\my.xml")
Here is a screenshot of Notepad++ from my.ps1 to show that my code does not contain any LFs
Here is a screenshot of Notepad++ from my.xml to show the LFs
One way to fix this, which I found, is using XmlWriterSettings and XmlTextWriter:
$settings = New-Object System.Xml.XmlWriterSettings
$settings.NewLineChars = "`r`n"
$settings.Indent = $true
$writer = [System.Xml.XmlTextWriter]::Create($dependencyXmlPath, $settings)
$myXml.Save($writer)
$writer.Close()
Is this the most simple solution?
Instruct the
[xml]
(System.Xml.XmlDocument
) instance to preserve insignificant whitespace before loading, by setting itsPreserveWhitespace
property to$true
, which preserves the input newline format as well as the specific intra-line whitespace[1] (note how the indentation changed in your output file).However, apart from preserving the indentation, the above only consistently results in Windows-format CRLF newlines if your
.ps1
file uses them (which it does):See:
.PreserveWhitespace
or, even with the latter, to ensure consistent use of a (possibly different) format.Generally - unless
.PreserveWhitespace = $true
is in effect - the.Save()
method:invariably uses the platform-native newline format, irrespective of the original input text's newline format...
... with one exception, which is the one you ran into:
Behind the scenes, the
XmlWriterSettings
instance that the.Save()
method uses (when not passed anXmlWriter
instance explicitly) has its.NewLineHandling
property set toNone
.This results in newlines that are part of multiline comments, multiline text nodes and other potentially multiline constructs such as CDATA sections getting serialized with LF-only newlines - always, irrespective of the original newline format in the input (presumably because in the in-memory DOM all newlines are stored in LF-only format).
This behavior is certainly surprising, and arguably a bug, given that it's therefore easy to end up with a mix of CRLF and LF newlines on Windows, as in your case.
A workaround without
.PreserveWhitespace = $true
and / or ensuring a consistent output newline format:Note the two use cases:
You may need need or want to preserve insignificant whitespace from the input on reading and are only concerned with ensuring consistent use of the newline format of interest on writing.
You need
.PreserveWhitespace = $true
but want to use a different newline format on writing.You can control the output newline format by explicitly creating a
XmlWriter
instance with with anXmlWriterSettings
instance with the following properties:For pretty-printing - if desired - set
.Indent = $true
, which uses 2 spaces per indentation level by default, overridable via.IndentChars
- this is what.Save()
does when given an output file path or stream.Set
.NewLineHandling = 'Replace'
to ensure consistent use of newlines (this is actually the default value, so it is curious that.Save()
in effect uses'None'
)..NewLineChars = "`r`n"
/.NewLineChars = "`n"
for Windows-format CRLF / Unix-format LF-only newlines.Testing a given file for the presence of LF-only newlines:
[1] There is one exception: intra-tag whitespace is not preserved, i.e. the specific whitespace - including any newlines - that separates the element name from the first attribute as well as the whitespace between attributes isn't preserved - see this answer.