在forloop中具有多个属性的Powershell访问XML节点

时间:2020-10-16 07:45:14

标签: powershell

xml示例

<?xml version="1.0" encoding="UTF-8"?>
<CUSTOMERS xml:lang="en">
    <CUSTOMER CREATED_DATE="2020-10-16 13:21:09.0" GROUP_ID="" ID="1509999">
        <NAME>
            <TITLE></TITLE>
            <FIRST_NAME>John</FIRST_NAME>
            <LAST_NAME>Smith</LAST_NAME>
        </NAME>
        <GENDER/>
        <DATE_OF_BIRTH/>
        <CONTACT_DETAILS>
            <TELEPHONE MARKETING_OPT_IN="F" TYPE="MOBILE">0123456789</TELEPHONE>
            <EMAIL MARKETING_OPT_IN="F">test@yahoo.com</EMAIL>
        </CONTACT_DETAILS>
        <ATTRIBUTE NAME="theCompany-News and offers on theCompany Womenswear_OPT_EMAIL">T</ATTRIBUTE>
    <ATTRIBUTE NAME="Email Soft Opt In_OPT_EMAIL">F</ATTRIBUTE>
        <ATTRIBUTE NAME="REGISTERED_ON_WEBSITE">T</ATTRIBUTE>
    </CUSTOMER>
   </CUSTOMERS>

我继承了Azure Powershell函数,该函数从XML文件构建值列表。 我需要添加一些代码来访问此值:

<ATTRIBUTE NAME="Email Soft Opt In_OPT_EMAIL">F</ATTRIBUTE>

这是我到目前为止拥有的Powershell代码

[xml]$xml = Get-Content C:\Users\Jason2\Desktop\XMLfolder\theSample.xml
$xml
$CustomerListFull = @()

foreach ($customer in $xml.CUSTOMERS.CUSTOMER) 
{           $BuildList = New-Object -TypeName psobject
            $BuildList | Add-Member -MemberType NoteProperty -Name TITLE -Value $customer.NAME.TITLE.Trim()
            $BuildList | Add-Member -MemberType NoteProperty -Name FIRSTNAME -Value $customer.NAME.FIRST_NAME.Trim()
        $BuildList | Add-Member -MemberType NoteProperty -Name LASTNAME -Value $customer.NAME.LAST_NAME.Trim()
            $BuildList | Add-Member -MemberType NoteProperty -Name EMAILCONSENT -Value "0"
            $BuildList | Add-Member -MemberType NoteProperty -Name EMAILSOFTOPTIN -Value "2"

    if ($customer.ATTRIBUTE.NAME -like "*OPT_EMAIL") {
                foreach ($value in $customer.ATTRIBUTE.NAME) {
                    
                        if ($value -match "theCompany-News and offers on theCompany Womenswear_OPT_EMAIL") {
                            $BuildList.EMAILCONSENT = "1"
                        }
                        if ($value -match "Email Soft Opt In_OPT_EMAIL") {
                        $BuildList.EMAILSOFTOPTIN = $customer.ATTRIBUTE.'#text'
                        }
               }
            }                 

$CustomerListFull += $BuildList
}
$CustomerListFull

这错误地给了我所有三个“属性名称”值(进入EMAILSOFTOPTIN字段)

TITLE          :
FIRSTNAME      : John
LASTNAME       : Smith
EMAILCONSENT   : 1
EMAILSOFTOPTIN : {T, F, T}

我尝试了几种尝试来获取值的方法,昨天的用户marsze向我展示了很棒的节点,但是我似乎无法使其在我的forloop中工作,我失败了。

$BuildList.EMAILSOFTOPTIN = $customer.SelectNodes("//*[local-name()='ATTRIBUTE'][@NAME='Email Soft Opt In_OPT_EMAIL']").InnerText

$BuildList.EMAILSOFTOPTIN = $customer.[ATTRIBUTE].[NAME='Email Soft Opt In_OPT_EMAIL').InnerText

$nodes = $xml.SelectNodes("//*[local-name()='ATTRIBUTE'][@NAME='Email Soft Opt In_OPT_EMAIL']")
$BuildList.EMAILSOFTOPTIN = $nodes.InnerText

请帮助使我摆脱困境

1 个答案:

答案 0 :(得分:1)

您的代码中存在多个小缺陷。

1。)

$customer.ATTRIBUTE.NAME -like "*OPT_EMAIL"

如果左操作数是一个集合,则结果也是一个集合,即所有对该操作返回true的项目,在这种情况下,NAME的所有值与*OPT_EMAIL"相匹配。另外,此检查也是不必要的,因为稍后您将进行另一个文字比较。

2。)

$value -match "Email Soft Opt In_OPT_EMAIL"

-Match 在这里可能不是一个好选择,因为它将正确的值视为正则表达式。在这种情况下可以使用,但我认为您想要-eq

3。)

$customer.ATTRIBUTE.'#text'

这将始终返回当前客户的所有<ATTRIBUTE>节点的内部文本的集合,即{T, F, T}

===

这是我的版本,进行了一些其他更改,但这只是个人喜好的问题:

[xml]$xml = Get-Content "C:\Users\Jason2\Desktop\XMLfolder\theSample.xml"
# you can build the array directly, like this
# because the += operator on arrays is terribly slow
$customerListFull = @(foreach ($customer in $xml.Customers.Customer) {
    # defaults
    $emailConsent = 0 # or $false?
    $emailSoftOptIn = "2"
    # loop attributes
    # the "OPT_EMAIL" check is dispensable here
    foreach ($attribute in $customer.Attribute) {
        # switch is a good replacement for a lot of if's,
        # that basically all do the same check
        switch ($attribute.Name) {
            "theCompany-News and offers on theCompany Womenswear_OPT_EMAIL" {
                $emailConsent = 1 # or $true?
            }
            "Email Soft Opt In_OPT_EMAIL" {
                $emailSoftOptIn = switch ($attribute.InnerText.Trim()) {
                    "F" { 0 }
                    "T" { 1 }
                }
                ### OR ###
                $emailSoftOptIn = @{F = 0; T = 1}[$attribute.InnerText.Trim()]
            }
        }
    }
    # Create and output the object directly.
    # This is an easier way to construct an object,
    # which also keeps the property order
    [PSCustomObject]@{
        Title = $customer.Name.Title.Trim()
        FirstName = $customer.Name.First_Name.Trim()
        LastName = $customer.Name.Last_Name.Trim()
        EmailConsent = $emailConsent
        EmailSoftOptIn = $emailSoftOptIn
    }
    ### OR ###
    # use [ordered]
    New-Object -TypeName PSObject -Property ([ordered]@{
        Title = $customer.Name.Title.Trim()
        FirstName = $customer.Name.First_Name.Trim()
        LastName = $customer.Name.Last_Name.Trim()
        EmailConsent = $emailConsent
        EmailSoftOptIn = $emailSoftOptIn
    })
})