Welcome to 16892 Developer Community-Open, Learning,Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

Apologies in advance as I have a very basic knowledge of Powershell, and even less knowledge of XML. But I have an XML file that I need to add a line to, and I'm trying to figure out how to do it with Powershell (because in the end, I'm going to have to do this a few thousand times). So file.xml is formatted as such:

<?xml version="1.0" encoding="utf-8"?>
<Sources>
  <add id="Source1" type="Test" distance="1">
    <parameters>
      <name />
      <address />
      <otherIdentifiers>FirstSource</otherIdentifiers>
      <supportedQueryTypes>Document</supportedQueryTypes>
      <host>10.5.19.194</host>
      <port>192</port>
    </parameters>
  </add>
  <add id="Source2" type="Prod" distance="2">
    <parameters>
      <name />
      <address />
      <otherIdentifiers>SecondSource</otherIdentifiers>
      <supportedQueryTypes>Document</supportedQueryTypes>
      <host>10.5.19.195</host>
      <port>193</port>
      <prod.baseUrl>http://SOURCE2PRODURL/Page</prod.baseUrl>
      <cache.enablePrefetch>false</cache.enablePrefetch>
    </parameters>
  </add>
  <add id="Source3" type="Prod" distance="3">
    <parameters>
      <name />
      <address />
      <otherIdentifiers>ThirdSource</otherIdentifiers>
      <supportedQueryTypes>Document</supportedQueryTypes>
      <host>10.5.19.197</host>
      <port>194</port>
      <prod.baseUrl>http://SOURCE3PRODURL/Page</prod.baseUrl>
      <cache.enablePrefetch>true</cache.enablePrefetch>
    </parameters>
  </add>
</Sources>

My goal is to find any source where type="Prod" and add the following line AFTER the prod.baseURL line:

<timeout>500</timeout>

So in the end, the section for Source2 and Source3 should essentially be formatted as:

  <add id="Source2" type="Prod" distance="2">
    <parameters>
      <name />
      <address />
      <otherIdentifiers>SecondSource</otherIdentifiers>
      <supportedQueryTypes>Document</supportedQueryTypes>
      <host>10.5.19.195</host>
      <port>193</port>
      <prod.baseUrl>http://SOURCE2PRODURL/Page</prod.baseUrl>
      <timeout>500</timeout>
      <cache.enablePrefetch>false</cache.enablePrefetch>
    </parameters>
  </add>
  <add id="Source3" type="Prod" distance="3">
    <parameters>
      <name />
      <address />
      <otherIdentifiers>ThirdSource</otherIdentifiers>
      <supportedQueryTypes>Document</supportedQueryTypes>
      <host>10.5.19.197</host>
      <port>194</port>
      <prod.baseUrl>http://SOURCE3PRODURL/Page</prod.baseUrl>
      <timeout>500</timeout>
      <cache.enablePrefetch>true</cache.enablePrefetch>
    </parameters>
  </add>

So this is what I've tried. If it makes absolutely no sense, I apologize, as it makes absolutely no sense to me...

$Path = .File.xml

[XML]$File = Get-Content -Path $Path -Raw

    $Sources = $File.Sources.add | Where-Object type -EQ "Prod"
    $URL = $Sources.parameters.{prod.baseurl}

    $Timeout = [XML]"@
        <add name='timeout' value='500'/>
        @"

    Foreach($Source in $Sources){
        $Timeout.InsertAfter($File.ImportNode($Timeout.parameters, $true), $URL)
        }
    $File.Save($Path)

And in the end, I'm getting:

Cannot convert value "@
        <add name='timeout' value='500'/>
        @" 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."

I realize this is ugly and totally wrong, but can anyone point me in the right direction?


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
3.8k views
Welcome To Ask or Share your Answers For Others

1 Answer

The problem is, when you use the easy dot notation for accessing XML elements, in some cases PowerShell converts these elements to other, simpler data types. In your case it converts prod.baseUrl element to String, because it contains only string content.

This is a problem as XmlElement.InsertAfter() requires arguments of type XmlNode, not String.

The solution uses XPath instead:

# Select child elements using XPath expression
$parameters = $File.SelectNodes("/Sources/add[@type='Prod']/parameters")

# For all found child elements
foreach( $param in $parameters ) {

    # Create timeout element 
    $timeout = $File.CreateElement('timeout')
    $timeout.InnerText = '500'

    # Insert timeout element after 'prod.baseUrl' element
    $param.InsertAfter( $timeout, $param.SelectSingleNode('prod.baseUrl') ) 
}

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to 16892 Developer Community-Open, Learning and Share
...