Powershell - xml removeall & looping



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

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

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_RootTESTsource5.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.仅删除了第一个提供程序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>

可能是我没有正确删除OrganizationUnitsRow,以便使用$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变量)是我们实现您的要求的地方,即查找地址不是所需地址类型(或没有给定地址类型)的任何组织/提供商。

  • '/OrganisationUnits/OrganisationUnitsRow-要返回的元素是OrganisationUnitsRow
  • [-我们筛选那些符合以下条件的行
  • not(-返回没有
  • ./Addresses/AddressesRow/AddressType-具有地址类型的地址行
  • /text()-带文本值
  • [{0}],它与我们传入的条件相匹配

我们放入文本路径的条件只是说"值在$var列表中的位置;并且是这样创建的:-$var | %{-对于$var中的每个值-". = '{0}'"-创建语句". = 'singleValueFromVar'"-($_ -replace "'","''")-NB:var中的值中的任何单引号都会导致问题,因此我们通过用''替换'来逃避它们。--加入'或'$xpath或'语句

即CCD_ 20被赋予值:

CCD_21

完整样本代码

$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

最新更新