4

I have a PowerShell script that runs a stored procedure which returns XML. I then export the XML into a file but when I open the file, each line has 3 dots at the end and the line isn't complete. This is with using out-file.

When I use Export-Clixml the XML that is returned from the query is dumped in a tag called <props> which is not one of my tags.

I am unsure where to go from here to save my XML in it's original format.

The PowerShell Script that I am using is similar to this:

$Date = Get-Date -format "yyyyMMdd_HHmm" 
$File = "C:\Temp\MyFile"+$Date+".xml"

$Query = "exec dbo.usp_MyProc"

Invoke-Sqlcmd -Query $Query -database MyDatabase -ServerInstance MyServer | out-file $File -Encoding utf8 
CJBS
  • 15,147
  • 6
  • 86
  • 135
Cornflake2068
  • 533
  • 1
  • 5
  • 17

7 Answers7

14

Assuming the XML returned from your SQL command is well formed XML, you could also push the XML string through .net's formatting (essentially a pretty printing of the XML).

function Format-XML {
  [CmdletBinding()]
  Param ([Parameter(ValueFromPipeline=$true,Mandatory=$true)][string]$xmlcontent)
  $xmldoc = New-Object -TypeName System.Xml.XmlDocument
  $xmldoc.LoadXml($xmlcontent)
  $sw = New-Object System.IO.StringWriter
  $writer = New-Object System.Xml.XmlTextwriter($sw)
  $writer.Formatting = [System.XML.Formatting]::Indented
  $xmldoc.WriteContentTo($writer)
  $sw.ToString()
}

$Date = Get-Date -format "yyyyMMdd_HHmm" 
$File = "C:\Temp\MyFile"+$Date+".xml"

$Query = "exec dbo.usp_MyProc"

Invoke-Sqlcmd -Query $Query -database MyDatabase -ServerInstance MyServer `
  | Format-XML `
  | Set-Content -Path $File -Force

Export-CliXml exports PowerShell XML, including type information, that can be loaded from disk to rehydrate a variable - hence the extra information.

Out-File has a default width, set by the host PowerShell environment. See Get-Help Out-File -Full.

An example without the XML formatting, storing the DataRow result and picking out the XML column.

$Date = Get-Date -format "yyyyMMdd_HHmm" 
$File = "C:\Temp\MyFile"+$Date+".xml"

$Query = "exec dbo.usp_MyProc"

$resultRow = Invoke-Sqlcmd -Query $Query -database MyDatabase -ServerInstance MyServer


$resultRow['XMLColumn'] | Set-Content -Path $File -Force

You'll obviously need to rename XMLColumn with the name of the column from your stored procedure.

TechSpud
  • 3,418
  • 1
  • 27
  • 35
  • I am getting an error with this " Exception calling "LoadXml" with "1" argument(s): "Data at the root level is invalid. Line 1, position 1." At line:6 char:3 + $xmldoc.LoadXml($xmlcontent) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + CategoryInfo : NotSpecified: (:) [], MethodInvocationException + FullyQualifiedErrorId : DotNetMethodException " – Cornflake2068 Sep 01 '16 at 13:01
  • Sounds like your XML isn't well formed (maybe multiple root nodes?). Use the example without the function and remove the line | Format-XML ` – TechSpud Sep 01 '16 at 14:33
  • it returns each line but instead of the XML it says "System.Data.DataRow" – Cornflake2068 Sep 01 '16 at 15:19
  • 1
    Sorry, thought I'd replied. Store the result in a variable, then pick out the XML data from a column in your DataRow. See amended example. – TechSpud Sep 02 '16 at 08:09
  • Thanks for that. This works, sort of. It only pulls out 4000 characters. I have tried adding -MaxLength to it but the Set-Content cmdlet doesn't recognise that command – Cornflake2068 Sep 02 '16 at 11:38
  • Is that because of the return type of your SQL stored procedure? Maybe it's set to NVARCHAR(4000) or VARCHAR(4000). If you can change the procedure, set it to return a VARCHAR(MAX) – TechSpud Sep 02 '16 at 11:48
  • Do you know who the author of `Format-XML` is? – Ehtesh Choudhury Dec 08 '17 at 22:54
  • No, sorry, but I guess a quick Google search might help? If I need to add the source/author, please let me know. It was a code snippet I’d saved a long time ago – TechSpud Dec 08 '17 at 22:56
2
function FormatXmlFile {
param (
    $filePath
)

$xml = Get-Content $filePath -Encoding Unicode
$xml = [xml]$xml
$Indent = 2
$StringWriter = New-Object System.IO.StringWriter
$XmlWriter = New-Object System.XMl.XmlTextWriter $StringWriter
$xmlWriter.Formatting = "indented"
$xmlWriter.Indentation = $Indent
$xml.WriteContentTo($XmlWriter)
$XmlWriter.Flush()
$StringWriter.Flush()
Set-Content -Path $filePath -Value $StringWriter.ToString()
}
Amrit Jain
  • 127
  • 1
  • 4
1
function pxml ($xmlfile) {
  $a = [xml](get-content $xmlfile)
  $a.save("$pwd\$xmlfile")
}
js2010
  • 23,033
  • 6
  • 64
  • 66
  • 1
    Your function is severely limited in that it only works with files located _in the current directory_. Also, consider adding an explanation of what your function does and why you named it that way. The `[xml] (get-content ...)` idiom is convenient, but [not fully robust](https://stackoverflow.com/a/71848130/45375). In a function, you can easily avoid it. – mklement0 Oct 11 '22 at 22:15
0

You need to convert the data into XML Try this:

[xml]$Result = Invoke-Sqlcmd -Query $Query -database MyDatabase -ServerInstance MyServer 
$Result | out-file $File -Encoding utf8 

or this

Invoke-Sqlcmd -Query $Query -database MyDatabase -ServerInstance MyServer |ConvertTo-XML |Out-File $File -Encoding utf8 
wonea
  • 4,783
  • 17
  • 86
  • 139
autosvet
  • 869
  • 1
  • 9
  • 17
0

Was really happy with @TechSpud's answer above, but found that empty XML nodes got introduced line breaks.

This version removes the linebreaks from empty XML nodes (or nodes just containing spaces) using a regex replace:

function Format-XML {
    [CmdletBinding()]
    Param ([Parameter(ValueFromPipeline=$true,Mandatory=$true)][string]$xmlcontent)
    $xmldoc = New-Object -TypeName System.Xml.XmlDocument
    $xmldoc.LoadXml($xmlcontent)
    $sw = New-Object System.IO.StringWriter
    $writer = New-Object System.Xml.XmlTextwriter($sw)
    $writer.Formatting = [System.XML.Formatting]::Indented
    $xmldoc.WriteContentTo($writer)
    $sw2 = $sw.ToString()
    $sw2 -replace '<(\w+)>[\s\r\n]*<\/\1>', "<`$1></`$1>"
}
widlov
  • 91
  • 1
  • 4
0

For an in-house script, an easy and pragmatic way to format XML is to pipe it through XML Starlet:

$myXmlObject.OuterXml | xml.exe format --indent-spaces 2

Or:

xml.exe format --indent-spaces 2 myXmlFile.txt
dan-gph
  • 16,301
  • 12
  • 61
  • 79
0

A slightly nicer way if you are ok to save to console out...

$xml = [xml] (gc c:\temp\powershell.xml)
$xml.Save([Console]::Out)

Credit to Dr. Scripto

8DH
  • 2,022
  • 23
  • 36