Powershell - xml removeall & 循环

Powershell - xml removeall & looping

我有一个源 XML 文件,我需要验证使用的地址类型是否与应用程序中使用的地址类型相匹配。如果 AddressType 与应用程序中定义的匹配,则一切正常,无需执行任何其他操作。 但是,如果 AddressType 不匹配,则从 XML 文件中删除整个提供程序。 我通过 rest 方法从应用程序中拉回类型代码并将它们放入数组中。 比较工作正常 - 当我删除提供者时出现问题。第一个提供者已从 xml 文件中正常删除,但其余 none 个提供者已被删除。

这些是来自应用程序的类型代码。

HQ
MAIN
NOT_STATED
OP

这是一个示例 xml 文件

 <?xml version="1.0" encoding="UTF-8"?>
<OrganisationUnits>
  <OrganisationUnitsRow num="1">
    <OrganisationId>ID1</OrganisationId>
    <OrganisationName>PROVIDER_1</OrganisationName>
    <Addresses>
      <AddressesRow num="1">
        <AddressType>TYPE1A</AddressType>
        <AddressTypeDesc>Head Office</AddressTypeDesc>
      </AddressesRow>
        <AddressesRow num="2">
        <AddressType>TYPE1B</AddressType>
        <AddressTypeDesc>Head Office</AddressTypeDesc>
      </AddressesRow>
    </Addresses>
  </OrganisationUnitsRow>

  <OrganisationUnitsRow num="2">
    <OrganisationId>ID2</OrganisationId>
    <OrganisationName>PROVIDER_2</OrganisationName>
    <Addresses>
      <AddressesRow num="1">
        <AddressType>TYPE2A</AddressType>
        <AddressTypeDesc>Head Office</AddressTypeDesc>
      </AddressesRow>
      <AddressesRow num="2">
        <AddressType>TYPE2B</AddressType>
        <AddressTypeDesc>Head Office</AddressTypeDesc>
      </AddressesRow>
    </Addresses>
  </OrganisationUnitsRow>

    <OrganisationUnitsRow num="3">
    <OrganisationId>ID3</OrganisationId>
    <OrganisationName>PROVIDER_3</OrganisationName>
    <Addresses>
      <AddressesRow num="3">
        <AddressType>TYPE3A</AddressType>
        <AddressTypeDesc>Head Office</AddressTypeDesc>
      </AddressesRow>
    </Addresses>
  </OrganisationUnitsRow>

</OrganisationUnits>

因此,在示例 xml 文件中,我有 5 行 AddressType 行(分布在 3 个不同的提供商中)与应用程序中的类型不匹配,因此应删除所有 3 个提供商。

这是我的代码片段。

#Define the source XML file path
$XMLPath = "$Provider_Root\TEST\source5.xml"
$xml = [xml](Get-Content $XMLPath)

## username and password to be used for web application login
$acctname = 'user1'
$password = 'letmein'

$params = @{uri = 'http://localhost:8080/providers/settings/provider/providerAddressTypes';
                   Method = 'Get'; #(or POST, or whatever)
                   Headers = @{Authorization = 'Basic ' + [Convert]::ToBase64String([Text.Encoding]::ASCII.GetBytes("$($acctname):$($password)"));
           } #end headers hash table
   } #end $params hash table

# This gets all the basic info ok
$var = invoke-restmethod @params

#show the values in the console
echo $var.code

#The app returns the following codes
#  HQ
#  MAIN
#  NOT_STATED
#  OP


#echo $var.Length
$total = $var.Length
write-host "AddressType records in application = $total"

#Count the number of AddressTypes that we are getting back from the app via the web call, if it is greater than zero, then we are getting data back ok.
if ($var.Length -gt 0)
{

    #Loop through the XML file looking for the AddressRow
    foreach($AddressRow in $xml.OrganisationUnits.OrganisationUnitsRow.Addresses.AddressesRow)
    {

        #Get the organisation ID - used for reporting purposes
        $OrgID = $xml.OrganisationUnits.OrganisationUnitsRow.OrganisationId

        #Get the root provider path so that we can delete it later
        $unitrow = $xml.OrganisationUnits.item('OrganisationUnitsRow')

        #Get the AddressType from the XML file in text format
        $n = $AddressRow.Item('AddressType')."#text"

        #Get the AddressType from the XML file
        $p = $AddressRow.Item('AddressType')



        #if the source XML file AddressType (stored in $n) is found in the array of app results (stored in an array $var.code) then we have a match and the provider is OK.
        if ($var.code -contains $n)
        #if ($var.code -eq $n)
        {
            echo "MATCH. xml source value is $n which matches a value in the app. Provider ID $OrgID"
        }
        # The XML file AddressType (stored in $n) is NOT found in the array of the app results (web query stored in an array $var.code) then the entire provider must be DELETED from the XML file.
        else
        {
            echo "NO MATCH. Source XML File value is $n. Provider ID $OrgID"

            #This removes the entire provider (I think)
            $unitrow.RemoveAll()    

            $xml.Save($XMLPath)
            $xml.Save($xml)
        }

    }

}
else
{
# No AddressType records were pulled back from the app, this could be an error.
echo "No AddressType records found in the app, this could be an error"

}

}

我的 powershell 脚本的控制台输出如下所示。

HQ
MAIN
NOT_STATED
OP
AddressType records in application = 4
NO MATCH. Source XML File value is TYPE1A. Provider ID ID1 ID2 ID3
NO MATCH. Source XML File value is TYPE1B. Provider ID ID2 ID3
NO MATCH. Source XML File value is TYPE2A. Provider ID ID2 ID3
NO MATCH. Source XML File value is TYPE2B. Provider ID ID2 ID3
NO MATCH. Source XML File value is TYPE3A. Provider ID ID2 ID3

所以问题是 1. 只去掉了第一个provider ID1。 2.它已经离开了原地。如果这些被手动删除并且脚本再次 运行 它会删除下一个提供者然后停止。

<?xml version="1.0" encoding="UTF-8"?>
<OrganisationUnits>
  <OrganisationUnitsRow>
  </OrganisationUnitsRow>
  <OrganisationUnitsRow num="2">
    <OrganisationId>ID2</OrganisationId>
    <OrganisationName>PROVIDER_2</OrganisationName>
    <Addresses>
      <AddressesRow num="1">
        <AddressType>TYPE2A</AddressType>
        <AddressTypeDesc>Head Office</AddressTypeDesc>
      </AddressesRow>
      <AddressesRow num="2">
        <AddressType>TYPE2B</AddressType>
        <AddressTypeDesc>Head Office</AddressTypeDesc>
      </AddressesRow>
    </Addresses>
  </OrganisationUnitsRow>
  <OrganisationUnitsRow num="3">
    <OrganisationId>ID3</OrganisationId>
    <OrganisationName>PROVIDER_3</OrganisationName>
    <Addresses>
      <AddressesRow num="3">
        <AddressType>TYPE3A</AddressType>
        <AddressTypeDesc>Head Office</AddressTypeDesc>
      </AddressesRow>
    </Addresses>
  </OrganisationUnitsRow>
</OrganisationUnits>

可能是我没有正确删除 OrganisationUnitsRow 以便使用 $unitrow.RemoveAll() 删除整个提供者,我现在看不到它。

显然,您在检查第一个子节点时尝试删除父节点,这样第二个子节点将不再存在。还有其他问题,但最好看一个正确且简单的例子。在外循环中,我们迭代可能被删除的实体,在内循环中,我们检查子项。

$IsModified = $false
foreach ($UnitRow in $xml.OrganisationUnits.OrganisationUnitsRow) {
    $OrgID = $UnitRow.OrganisationId
    foreach ($AddressRow in $UnitRow.Addresses.AddressesRow) {
        $n = $AddressRow.AddressType
        if ($var.code -contains $n) {
            echo "MATCH. blabla"
        } else {
            echo "NO MATCH. blabla"
            $UnitRow.parentNode.RemoveChild($UnitRow) >$null
            $IsModified = $true
            break
        }
    }
}

if ($IsModified) {
    $xml.Save($XMLPath)
}

这是一个快速解决方案:

if ($var.Count -gt 0) {
    [string]$xpath = '/OrganisationUnits/OrganisationUnitsRow[not(./Addresses/AddressesRow/AddressType/text()[{0}])]' -f (($var | %{". = '{0}'" -f ($_ -replace "'","''")}) -join ' or ')
    $xml.SelectNodes($xpath) | %{$_.RemoveAll()} 
}
#output result to console to show what's been done
$xml | Format-Xml

说明

  • $xml.SelectNodes($xpath) - 找到所有满足我们条件的节点
  • %{$_.RemoveAll()} - 并删除它们

上面提到的标准(即 $xpath 变量)是我们实现您的要求的地方,即找到任何 organisations/providers 地址不属于所需地址类型(或没有地址)的人给定的类型)。

  • '/OrganisationUnits/OrganisationUnitsRow - 要 return 编辑的元素是 OrganisationUnitsRow
  • [ - 我们筛选满足以下条件的那些行
  • not( - return 没有的行
  • ./Addresses/AddressesRow/AddressType - 地址类型为
  • 的地址行
  • /text() - 带有文本值
  • [{0}] 符合我们传入的条件

我们放入文本路径的条件只是说“值在 $var 列表中的位置;并且是这样创建的: - $var | %{ - 对于 $var 中的每个值 - ". = '{0}'" - 创建语句 ". = 'singleValueFromVar'" - ($_ -replace "'","''") - 注意:var 值中的任何单引号都会导致问题,因此我们通过将 ' 替换为 '' 来避免它们。 - </code>-加入'或'<code>- join all the values together with或`语句

$xpath 赋值:

/OrganisationUnits/OrganisationUnitsRow[not(./Addresses/AddressesRow/AddressType/text()[. = 'HQ' or . = 'MAIN' or . = 'NOT_STATED' or . = 'OP'])]

完整示例代码

$xml = [xml]@"
<OrganisationUnits>
  <OrganisationUnitsRow>
  </OrganisationUnitsRow>
  <OrganisationUnitsRow num="2">
    <OrganisationId>ID2</OrganisationId>
    <OrganisationName>PROVIDER_2</OrganisationName>
    <Addresses>
      <AddressesRow num="1">
        <AddressType>TYPE2A</AddressType>
        <AddressTypeDesc>Head Office</AddressTypeDesc>
      </AddressesRow>
      <AddressesRow num="2">
        <AddressType>TYPE2B</AddressType>
        <AddressTypeDesc>Head Office</AddressTypeDesc>
      </AddressesRow>
    </Addresses>
  </OrganisationUnitsRow>
  <OrganisationUnitsRow num="3">
    <OrganisationId>ID3</OrganisationId>
    <OrganisationName>PROVIDER_3</OrganisationName>
    <Addresses>
      <AddressesRow num="3">
        <AddressType>HQ</AddressType>
        <AddressTypeDesc>Head Office</AddressTypeDesc>
      </AddressesRow>
    </Addresses>
  </OrganisationUnitsRow>
</OrganisationUnits>
"@

clear-host
[string[]]$var = @('HQ','MAIN','NOT_STATED','OP')
if ($var.Count -gt 0) {
    [string]$xpath = '/OrganisationUnits/OrganisationUnitsRow[not(./Addresses/AddressesRow/AddressType/text()[{0}])]' -f (($var | %{". = '{0}'" -f ($_ -replace "'","''")}) -join ' or ')
    $xml.SelectNodes($xpath) | %{$_.RemoveAll()} 
}
#output result to console to show what's been done
$xml | Format-Xml