I am writing a powershell script to create a xml file which i later feed into azure pipeline. Issue is this generate an encoded output with < , > being converted, which is not in a correct xml format I understand this is related to automatic encoding. Some help to prevent this is appreciated
$myitems =
@([pscustomobject]
@{AssertName="Joe";TestPass=$true;},
[pscustomobject]
@{AssertName="Sue";TestPass=$false;},
[pscustomobject]
@{AssertName="Cat";TestPass=$true;})
Set-Content $path <?xml version="1.0"?><testsuites></testsuites>'
$xml = New-Object XML
$xml.Load($path)
$element = $xml.SelectSingleNode("testsuites")
$innerText=""
foreach ($item in $myitems )
{
$innerText=$innerText + '<testsuite errors="0" failures="0" id="0" name="$item.AssertName" tests="1"><testcase classname="some.class.name" name="Test1" time="123.345000"/></testsuite>'
}
[xml] $xml = Get-Content -Raw $path
$xml.testsuites = $innerText
$xml.Save($path)
I strongly suggest to avoid working with raw XML strings and instead build the whole XML document element by element, using .NET API. This way you can just write any data as-is and the API makes sure of the proper XML encoding.
In general there are two kind of API for building XML:
XmlDocument
(type accelerator [xml]
). This one is easiest to use, but is comparatively slow and stores the whole document in memory, which can be an issue for really large documents.XmlWriter
. This is the fastest way and has the lowest memory footprint. It is more cumbersome to use, as you have to take care that elements are properly closed. Also you can't create the elements out of order, they are written in the order you call the API.$myitems = @(
[pscustomobject] @{AssertName="Joe";TestPass=$true}
[pscustomobject] @{AssertName="Sue";TestPass=$false}
[pscustomobject] @{AssertName="Cat";TestPass=$true}
)
$xml = [xml]::new()
$null = $xml.AppendChild( $xml.CreateXmlDeclaration('1.0', 'utf-8', $null) )
$root = $xml.AppendChild( $xml.CreateElement('testsuites') )
foreach ($item in $myitems )
{
$testSuite = $root.AppendChild( $xml.CreateElement('testsuite') )
$testSuite.SetAttribute('errors', 0)
$testSuite.SetAttribute('failures', 0)
$testSuite.SetAttribute('id', 0)
$testSuite.SetAttribute('name', $item.AssertName)
$testSuite.SetAttribute('tests', 1)
$testCase = $testSuite.AppendChild( $xml.CreateElement('testcase') )
$testCase.SetAttribute('classname', 'some.class.name')
$testCase.SetAttribute('name', 'Test1')
$testCase.SetAttribute('time', '123.345000')
}
$xml.Save( "$PSScriptRoot\test.xml" )
Output:
<?xml version="1.0" encoding="utf-8"?>
<testsuites>
<testsuite errors="0" failures="0" id="0" name="Joe" tests="1">
<testcase classname="some.class.name" name="Test1" time="123.345000" />
</testsuite>
<testsuite errors="0" failures="0" id="0" name="Sue" tests="1">
<testcase classname="some.class.name" name="Test1" time="123.345000" />
</testsuite>
<testsuite errors="0" failures="0" id="0" name="Cat" tests="1">
<testcase classname="some.class.name" name="Test1" time="123.345000" />
</testsuite>
</testsuites>
$path = "$PSScriptRoot\test.xml"
$myitems = @(
[pscustomobject] @{AssertName="Joe";TestPass=$true}
[pscustomobject] @{AssertName="Sue";TestPass=$false}
[pscustomobject] @{AssertName="Cat";TestPass=$true}
)
$writerSettings = [Xml.XmlWriterSettings] @{
Encoding = [Text.Encoding]::UTF8
Indent = $true
IndentChars = "`t"
WriteEndDocumentOnClose = $true # Write document end tag automatically
}
$writer = [xml.XmlWriter]::Create( $path, $writerSettings )
$writer.WriteStartDocument() # writes the XML declaration
$writer.WriteStartElement('testsuites')
foreach ($item in $myitems )
{
# Indentation is used to show the nesting of the XML elements
$writer.WriteStartElement('testsuite')
$writer.WriteAttributeString('errors', 0)
$writer.WriteAttributeString('failures', 0)
$writer.WriteAttributeString('id', 0)
$writer.WriteAttributeString('name', $item.AssertName)
$writer.WriteAttributeString('tests', 1)
$writer.WriteStartElement('testcase')
$writer.WriteAttributeString('classname', 'some.class.name')
$writer.WriteAttributeString('name', 'Test1')
$writer.WriteAttributeString('time', '123.345000')
$writer.WriteEndElement()
$writer.WriteEndElement()
}
# Very important - writes document end tag and closes the file
$writer.Dispose()
Output:
<?xml version="1.0" encoding="utf-8"?>
<testsuites>
<testsuite errors="0" failures="0" id="0" name="Joe" tests="1">
<testcase classname="some.class.name" name="Test1" time="123.345000" />
</testsuite>
<testsuite errors="0" failures="0" id="0" name="Sue" tests="1">
<testcase classname="some.class.name" name="Test1" time="123.345000" />
</testsuite>
<testsuite errors="0" failures="0" id="0" name="Cat" tests="1">
<testcase classname="some.class.name" name="Test1" time="123.345000" />
</testsuite>
</testsuites>