1

I want to replace the URL contained within an XML from "http://example.com/webapi/" to "http://example.com/webapi_v2/". However, I get the following error: "The property 'InnerText' cannot be found on this object. Verify that the property exists and can be set.". The code works fine when it is just text. But not when there is a URL present.

My code is as follows:

$file = "C:\Users\Desktop\test_XML.xml"
$xmldata = [xml] (Get-Content $file)
$xmldata.SystemSettings.Authentication.WebService.InnerText = 'http://example.com/webapi_v2/'
$xmldata.Save((Resolve-Path $file).Path)

And here is the XML:

<?xml version="1.0" encoding="UTF-8"?>
<SystemSettings>
    <Authentication>
        <WebService>http://example.com/webapi/</WebService>
    </Authentication>
</SystemSettings>
Kerbol
  • 588
  • 2
  • 9
  • 24
  • 2
    The xml type accelerator is doing some of the heavy lifting for you - ```WebService``` is a *string* property it's added automatically (you can see this if you do ```$xmldata.SystemSettings.Authentication | gm``` you'll see ```WebService Property string WebService {get;set;}``` in the output). Just do ```$xmldata.SystemSettings.Authentication.WebService = 'http://example.com/webapi_v2'``` without the ```InnerText``` and you should be golden... – mclayton Mar 27 '23 at 12:14

2 Answers2

2

I modified your script a bit (deleted InnerText) and it worked for me:

[xml] $xmldata = Get-Content $file
$xmldata.SystemSettings.Authentication.WebService = "http://example.com/webapi_v2/"
Kapitany
  • 1,319
  • 1
  • 6
  • 10
2

Kapitany's helpful answer is effective, and mclayton's comment on the question provides additional information, but let me provide systematic background information:

PowerShell's adaptation of the XML DOM surfaces XML child elements and attributes as object properties, in addition to the type-native properties of the .NET types being adapted, [xml] (System.Xml.XmlDocument) and System.Xml.XmlElement.

Because your <WebService> element is a simple XML element - one that has neither attributes nor child elements - the property referring to it was surfaced as a [string] value rather than as a System.Xml.XmlElement instance, so as to make setting and getting mere text content easier.

That is, a property representing a simple XML element:

  • on getting: implicitly returns the value of the underlying XmlElement's .InnerText property.

  • on setting: implicitly updates the .InnerText property.

Only properties adapting complex elements - ones that have at least one attribute or child element - surface as (adapted) XmlElement instances, and only then can you access type-native properties such as .InnerText explicitly.

For a comprehensive overview of PowerShell's adaptation of the XML DOM, see this answer.


To illustrate the difference:

  • Example use of an adapted property representing a simple XML element:

    • <WebService> is a simple element, because it neither has attributes nor child elements.
# Getting / setting a property representing a SIMPLE element
# returns / updates the element's native .InnerText property value.

# Get: -> 'http://example.com/webapi/'
$xmldata.SystemSettings.Authentication.WebService

# Set
$xmldata.SystemSettings.Authentication.WebService = 'http://example.com/webapi_v2/'
  • Example use of an adapted property representing a complex XML element:

    • <Authentication> is a complex element, because it has a child element.

    • Getting such an element via an adapted property returns it as its own type, i.e. as an (adapted) XmlElement instance.

    • Setting requires manipulating that instance via its type-native members (properties and methods).

    • The following is the equivalent of the above, using the type-native parameterized .Item() property to get the <WebService> child element by name (could also be expressed with index syntax, namely as $xmldata.SystemSettings.Authentication['WebService']):

      • Because a type-native property is used to retrieve it, <WebService> (even though in PowerShell terms it is a simple element) is returned by its native type too (XmlElement), requiring explicit use of .InnerText to get / set its text content.

      • You can use this technique if you explicitly want to gain access to a simple XML element as an XmlElement instance, so that you can add Xml attributes, for instance.

# Get: -> 'http://example.com/webapi/'
$xmldata.SystemSettings.Authentication.Item('WebService').InnerText

# Set
$xmldata.SystemSettings.Authentication.Item('WebService').InnerText = 'http://example.com/webapi_v2/'
mklement0
  • 382,024
  • 64
  • 607
  • 775