简体   繁体   中英

using powershell to set attribute inside an XML node

I have an Xml File which include nodes that looks like:

<Installation>
    <VersionData Type="Upgrade" 
    </VersionData>
    <DeploymentData>
        <DataFileGroup enable="True">
            <DataFileItem name="file2.exe.config" enable="True" installMode="Both" sourceFolder="C:\files\" distributionType="AddReplace" >
                <ItemTarget featureName="AppServer" targetPaths="$targetPath/folder3;"/>
                <ItemTarget featureName="WebServer" targetPaths="$targetPath/folder1;"/>
                <ItemTarget featureName="DBServer" targetPaths="$targetPath;"/>
            </DataFileItem>
        </DataFileGroup>
    </DeploymentData>
</Installation>

$xmlFile = "C:\work\myXML.xml"
$xml = [xml](Get-Content $xmlFile)  
$xml.Load($xmlFile)

First I need to to get the value of targetPaths where featureName is DBServer . Then I want to change the value of:

ItemTarget featureName="DBServer" targetPaths="$targetPath;"   

to

ItemTarget featureName="DBServer" targetPaths="$targetPath;$concate_TargetPath"

First, you will need to fix your XML by changing

<VersionData Type="Upgrade" 
</VersionData>

to

<VersionData Type="Upgrade" />

Once you have a valid xml file, you can do this to update the attribute values:

$xmlFile = "C:\work\myXML.xml"
[xml]$xml = Get-Content $xmlFile -Raw

# find all nodes 'ItemTarget' with featureName 'DBServer'
$nodes =  $xml.SelectNodes("//ItemTarget[@featureName='DBServer']")
foreach ($node in $nodes) {
    # change the value of the 'targetPaths' attribute
    # because of the dollar signs, use single quotes here if this should be the literal value.
    # if you mean to have these values expanded from variables with these names, use double quotes
    $node.targetPaths = '$targetPath;$concate_TargetPath'
}

# save the changes to a new xml file
$xml.Save('C:\work\myXML_Updated.xml')


UPDATE: Finding nodes to update using XPath


From your comment, I understand there is more to it than was clear in the original question.

If you need to only change the targetPaths attribute for ItemTarget nodes that are in node DataFileItem which has an attribute called name and the value thereof is equal to a specific value, THEN the targetPaths attribute needs to be changed by adding a new path to it IF this new path is not already present. (Am I correct so far?)

In that case, try this:

 # this is the name attribute to search for the DataFileItem node $dataFileName = "file2.exe.config" # this is the path to add to the 'targetPaths' attribute if not already present $newPath = "SomeNewPathToAppend" $xmlFile = 'C:\\work\\myXML.xml' [xml]$xml = Get-Content $xmlFile -Raw # find all nodes 'ItemTarget' with featureName 'DBServer' within the 'DataFileItem' node that has attribute $dataFileName $nodes = $xml.SelectNodes("//DataFileItem[@name='$dataFileName']/ItemTarget[@featureName='DBServer']") foreach ($node in $nodes) { # split the current value by the semicolon and remove empty elements $paths = $node.targetPaths.Split(";", [System.StringSplitOptions]::RemoveEmptyEntries) # check if the $newPath is not already in this array if ($paths -notcontains $newPath) { # change the value of the 'targetPaths' attribute by adding the $newPath to it $paths += $newPath $node.targetPaths = "{0};" -f ($paths -join ';') } } # save the changes to a new xml file $xml.Save('C:\\work\\myXML_Updated.xml') 


UPDATE: Finding nodes to update Case Insensitively


Because the above code uses Case Sensitive XPath to find the nodes you want to update, it cannot handle cases where any of the name or featureName should be compared in a case insensitive manner.

So here's a new approach that will get you the items to update using case insensitive comparison:

 # this is the name attribute to search for the DataFileItem node $dataFileName = "file2.exe.config" # this is the path to add to the 'targetPaths' attribute if not already present $newPath = "SomeNewPathToAppend" $xmlFile = 'C:\\work\\myXML.xml' [xml]$xml = Get-Content $xmlFile -Raw # find all 'DataFileItem' nodes that have attribute 'name' equal to $dataFileName (case insensitive) $nodes = $xml.GetElementsByTagName("DataFileItem") | Where-Object { $_.name -eq $dataFileName } # or do it like this: # $nodes = $xml.ChildNodes.DeploymentData.DataFileGroup.DataFileItem | Where-Object { $_.name -eq $dataFileName } # within these 'DataFileItem' nodes, find all 'ItemTarget' elements where attribute 'featureName' equals "DBServer" (case insensitive) $nodes.ItemTarget | Where-Object { $_.featureName -eq "DBServer" } | ForEach-Object { # split the current value by the semicolon and remove empty elements $paths = $_.targetPaths.Split(";", [System.StringSplitOptions]::RemoveEmptyEntries) # check if the $newPath is not already in this array if ($paths -notcontains $newPath) { # change the value of the 'targetPaths' attribute by adding the $newPath to it $paths += $newPath $_.targetPaths = "{0};" -f ($paths -join ';') } } # save the changes to a new xml file $xml.Save('C:\\work\\myXML_Updated.xml') 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM