Search code examples
xmlpowershellinvoke-webrequest

PowerShell ConvertTo-Xml vs [XML] Accelerator Type


I'm executing an HTTP Get request (via Invoke-WebRequest) against a website, that's in turn, is returning the following XML structure:

<?xml version="1.0" encoding="utf-8"?>
<ROOT xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <VENDOR>
    <SUBJECT>Check Oil</SUBJECT>
    <VENDORID>NA</VENDORID>
    <VANTIVE>2</VANTIVE>
    <MODEL>I</MODEL>
    <DESCRIPTION>Success</DESCRIPTION>
  </VENDOR>
  <VENDOR>
    <SUBJECT>Check Lights</SUBJECT>
    <SIGN>N</SIGN>
    <VENDORID>NA</VENDORID>
    <VANTIVE>2</VANTIVE>
    <MODEL>I</MODEL>
    <DESCRIPTION>Success</DESCRIPTION>
  </VENDOR>
  <VENDOR>
    <SUBJECT>Check Engine</SUBJECT>
    <SIGN>N</SIGN>
    <VENDORID>NA</VENDORID>
    <VANTIVE>2</VANTIVE>
    <MODEL>I</MODEL>
    <DESCRIPTION>Success</DESCRIPTION>
  </VENDOR>
</ROOT> 

When trying to parse the XML data, using the ConvertTo-Xml cmdlet:

$result = Invoke-WebRequest -Uri 'https://mycars.com/list/cars' -UseBasicParsing -Method Get

$resultinxml = $result.Content | ConvertTo-Xml 
 
$resultinxml.Objects.Object.'#text' 

Everything is working properly.

But when I'm trying to use the [xml] accelerator type instead:

$result = Invoke-WebRequest -Uri 'https://mycars.com/list/cars' -UseBasicParsing -Method Get

[xml] $resultinxml = $result.Content

The following error is occurred:

Cannot convert value "<?xml version="1.0" encoding="utf-8"?>
<ROOT xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
  <VENDOR>
    <SUBJECT>Check Oil</SUBJECT>
    <VENDORID>NA</VENDORID>
    <VANTIVE>2</VANTIVE>
    <MODEL>I</MODEL>
    <DESCRIPTION>Success</DESCRIPTION>
  </VENDOR>
  <VENDOR>
    <SUBJECT>Check Lights</SUBJECT>
    <SIGN>N</SIGN>
    <VENDORID>NA</VENDORID>
    <VANTIVE>2</VANTIVE>
    <MODEL>I</MODEL>
    <DESCRIPTION>Success</DESCRIPTION>
  </VENDOR>
  <VENDOR>
    <SUBJECT>Check Engine</SUBJECT>
    <SIGN>N</SIGN>
    <VENDORID>NA</VENDORID>
    <VANTIVE>2</VANTIVE>
    <MODEL>I</MODEL>
    <DESCRIPTION>Success</DESCRIPTION>
  </VENDOR>
</ROOT> 
" to type "System.Xml.XmlDocument". Error: "The specified node cannot be inserted as the valid child of this node, because the specified node is the wrong type."
At line:1 char:1
+ $webreqcontent = [xml] ($result.Content)
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : InvalidArgument: (:) [], RuntimeException
    + FullyQualifiedErrorId : InvalidCastToXmlDocument

Which is very odd, as the XML structure hasn't been changed and is XML valid.

Do accelerator types in PowerShell has some prerequisites or limitations, that could cause this issue?


Solution

  • Building on the helpful comments on the question:

    When trying to parse the XML data, using the ConvertTo-Xml cmdlet:

    ConvertTo-Xml does not parse XML, it creates XML representations (serialization) of arbitrary .NET instances - and since the resulting XML format is neither documented nor is there a complementary cmdlet to interpret (deserialize) it, this cmdlet is virtually useless - see GitHub issue #8311.
    GitHub issue #3898 is a green-lit proposal to introduce ConvertTo-CliXml and ConvertFrom-CliXml cmdlets, instead, which will use the standardized CLIXML format that PowerShell uses for cross-process serialization, such as in remoting.

    In other words: your use of ConvertTo-Xml won't work as intended.


    If you use Invoke-RestMethod instead of Invoke-WebRequest, XML parsing is built in (with web services that return XML) and directly returns a [xml] (System.Xml.XmlDocument) instance.

    # If the web-service call returns XML, Invoke-RestMethod automatically
    # parses it into a [xml] instance.
    $resultXml = Invoke-RestMethod -Uri 'https://mycars.com/list/cars' 
    

    However, your error message suggests that you have one or more extraneous characters preceding the XML text - even though, curiously, that isn't reflected in the error message, at least as shown in your question.

    • A simple way to provoke the error is with [xml] '!<foo/>'

    Conceivably, the XML text is preceded by a BOM (byte order mark), even though it normally shouldn't in this context.

    • If that is the problem, you can try the following:
    $result = Invoke-WebRequest -Uri 'https://mycars.com/list/cars' -UseBasicParsing
    
    # Skip the BOM character .
    [xml] $resultinxml = $result.Content.Substring(1)
    
    • If it isn't, you'll need to examine the XML text for hidden control characters, as shown in this answer.

    A solution to a related problem - an XML response getting misinterpreted due to a character-encoding mismatch - can be found in this answer.