最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Building XML with PowerShell - Stack Overflow

programmeradmin2浏览0评论

I've inherited this script that builds xml with users and their roles. It doesn't handle the situation where one user can have multiple roles.

Here is an excerpt from the script:

$somePATH = "C:\wherever"
"Dummy Header" | Set-Content $somePATH


$data = @(
   [pscustomobject]@{Type='P';ID='Fred';Role='Operator';Domain='Home'}
   [pscustomobject]@{Type='P';ID='Fred';Role='Admin';Domain='Home'}
   [pscustomobject]@{Type='P';ID='Wilma';Role='Operator';Domain='Home'}
   [pscustomobject]@{Type='P';ID='Barney';Role='Admin';Domain='Work'}
   [pscustomobject]@{Type='N';ID='Betty';Role='Whatev';Domain='Home'}
)

Function BuildXML
{
param($Type, $ID, $Role, $Domain)
$CC = "1234"
$Account = "SNOW_CC$CC" + "_BNK0000_BLAH"

if ($Type -eq "N") {

@”
       <account id="$Account">
       <name><![CDATA[$ID@$Domain]]></name>
           <endPoint>UNC</endPoint>
           <domain>UNC</domain>
           <comments/>
           <attributes>
               <attribute name="appUserName">
                   <attributeValues>
                       <attributeValue><value><![CDATA[$ID]]></value></attributeValue>
                   </attributeValues>
               </attribute>
               <attribute name="CostCentre">
                   <attributeValues>
                       <attributeValue><value><![CDATA[$CC]]></value></attributeValue>
                   </attributeValues>
               </attribute>
               <attribute name="Bank_Number">
                   <attributeValues>
                       <attributeValue><value><![CDATA[0000]]></value></attributeValue>
                   </attributeValues>
               </attribute>
               <attribute name="Directory">
                   <attributeValues>
                       <attributeValue><value><![CDATA[BLAH]]></value></attributeValue>
                   </attributeValues>
               </attribute>
               <attribute name="Role">
                   <attributeValues><attributeValueRef id="Role=$Role"/></attributeValues>
               </attribute>
           </attributes>
       </account>
"@ | Add-Content $somePATH


   } elseif ($Type -eq "P") {   

@"
       <account id="$ID">
           <name><![CDATA[$ID@$Domain]]></name>
           <endPoint>ABC</endPoint>
           <domain>ABC</domain>
           <comments/>
           <attributes>
               <attribute name="AppBoRID">
                   <attributeValues>
                       <attributeValue><value><![CDATA[$ID]]></value></attributeValue>
                   </attributeValues>
               </attribute>
               <attribute name="Role">
                   <attributeValues><attributeValueRef id="Role=$Role"/></attributeValues>
               </attribute>
           </attributes>
       </account>
"@ | Add-Content $WORKPATH$FEEDFILENAME
   
   }
} # End Function


foreach ($d in $data) {BuildXML $d.Type $d.ID $d.Role $d.Domain}

Showing results just for the 'Fred' user:

        <account id="Fred">
            <name><![CDATA[Fred@Home]]></name>
            <endPoint>ABC</endPoint>
            <domain>ABC</domain>
            <comments/>
            <attributes>
                <attribute name="AppBoRID">
                    <attributeValues>
                        <attributeValue><value><![CDATA[Fred]]></value></attributeValue>
                    </attributeValues>
                </attribute>
                <attribute name="Role">
                    <attributeValues><attributeValueRef id="Role=Operator"/></attributeValues>
                </attribute>
            </attributes>
        </account>
        <account id="Fred">
            <name><![CDATA[Fred@Home]]></name>
            <endPoint>ABC</endPoint>
            <domain>ABC</domain>
            <comments/>
            <attributes>
                <attribute name="AppBoRID">
                    <attributeValues>
                        <attributeValue><value><![CDATA[Fred]]></value></attributeValue>
                    </attributeValues>
                </attribute>
                <attribute name="Role">
                    <attributeValues><attributeValueRef id="Role=Admin"/></attributeValues>
                </attribute>
            </attributes>
        </account>

What I'm after is something like this, where 'Fred' would only have one entry, but it reflects both 'Roles':

       <account id="Fred">
            <name><![CDATA[Fred@Home]]></name>
            <endPoint>ABC</endPoint>
            <domain>ABC</domain>
            <comments/>
            <attributes>
                <attribute name="AppBoRID">
                    <attributeValues>
                        <attributeValue><value><![CDATA[Fred]]></value></attributeValue>
                    </attributeValues>
                </attribute>
                <attribute name="Role">
                    <attributeValues><attributeValueRef id="Role=Operator"/></attributeValues>
                    <attributeValues><attributeValueRef id="Role=Admin"/></attributeValues>
                </attribute>
            </attributes>
        </account>

I'm at a loss as to how to get the multiple roles reflected in a single user entry.

Appreciate any help.

I've inherited this script that builds xml with users and their roles. It doesn't handle the situation where one user can have multiple roles.

Here is an excerpt from the script:

$somePATH = "C:\wherever"
"Dummy Header" | Set-Content $somePATH


$data = @(
   [pscustomobject]@{Type='P';ID='Fred';Role='Operator';Domain='Home'}
   [pscustomobject]@{Type='P';ID='Fred';Role='Admin';Domain='Home'}
   [pscustomobject]@{Type='P';ID='Wilma';Role='Operator';Domain='Home'}
   [pscustomobject]@{Type='P';ID='Barney';Role='Admin';Domain='Work'}
   [pscustomobject]@{Type='N';ID='Betty';Role='Whatev';Domain='Home'}
)

Function BuildXML
{
param($Type, $ID, $Role, $Domain)
$CC = "1234"
$Account = "SNOW_CC$CC" + "_BNK0000_BLAH"

if ($Type -eq "N") {

@”
       <account id="$Account">
       <name><![CDATA[$ID@$Domain]]></name>
           <endPoint>UNC</endPoint>
           <domain>UNC</domain>
           <comments/>
           <attributes>
               <attribute name="appUserName">
                   <attributeValues>
                       <attributeValue><value><![CDATA[$ID]]></value></attributeValue>
                   </attributeValues>
               </attribute>
               <attribute name="CostCentre">
                   <attributeValues>
                       <attributeValue><value><![CDATA[$CC]]></value></attributeValue>
                   </attributeValues>
               </attribute>
               <attribute name="Bank_Number">
                   <attributeValues>
                       <attributeValue><value><![CDATA[0000]]></value></attributeValue>
                   </attributeValues>
               </attribute>
               <attribute name="Directory">
                   <attributeValues>
                       <attributeValue><value><![CDATA[BLAH]]></value></attributeValue>
                   </attributeValues>
               </attribute>
               <attribute name="Role">
                   <attributeValues><attributeValueRef id="Role=$Role"/></attributeValues>
               </attribute>
           </attributes>
       </account>
"@ | Add-Content $somePATH


   } elseif ($Type -eq "P") {   

@"
       <account id="$ID">
           <name><![CDATA[$ID@$Domain]]></name>
           <endPoint>ABC</endPoint>
           <domain>ABC</domain>
           <comments/>
           <attributes>
               <attribute name="AppBoRID">
                   <attributeValues>
                       <attributeValue><value><![CDATA[$ID]]></value></attributeValue>
                   </attributeValues>
               </attribute>
               <attribute name="Role">
                   <attributeValues><attributeValueRef id="Role=$Role"/></attributeValues>
               </attribute>
           </attributes>
       </account>
"@ | Add-Content $WORKPATH$FEEDFILENAME
   
   }
} # End Function


foreach ($d in $data) {BuildXML $d.Type $d.ID $d.Role $d.Domain}

Showing results just for the 'Fred' user:

        <account id="Fred">
            <name><![CDATA[Fred@Home]]></name>
            <endPoint>ABC</endPoint>
            <domain>ABC</domain>
            <comments/>
            <attributes>
                <attribute name="AppBoRID">
                    <attributeValues>
                        <attributeValue><value><![CDATA[Fred]]></value></attributeValue>
                    </attributeValues>
                </attribute>
                <attribute name="Role">
                    <attributeValues><attributeValueRef id="Role=Operator"/></attributeValues>
                </attribute>
            </attributes>
        </account>
        <account id="Fred">
            <name><![CDATA[Fred@Home]]></name>
            <endPoint>ABC</endPoint>
            <domain>ABC</domain>
            <comments/>
            <attributes>
                <attribute name="AppBoRID">
                    <attributeValues>
                        <attributeValue><value><![CDATA[Fred]]></value></attributeValue>
                    </attributeValues>
                </attribute>
                <attribute name="Role">
                    <attributeValues><attributeValueRef id="Role=Admin"/></attributeValues>
                </attribute>
            </attributes>
        </account>

What I'm after is something like this, where 'Fred' would only have one entry, but it reflects both 'Roles':

       <account id="Fred">
            <name><![CDATA[Fred@Home]]></name>
            <endPoint>ABC</endPoint>
            <domain>ABC</domain>
            <comments/>
            <attributes>
                <attribute name="AppBoRID">
                    <attributeValues>
                        <attributeValue><value><![CDATA[Fred]]></value></attributeValue>
                    </attributeValues>
                </attribute>
                <attribute name="Role">
                    <attributeValues><attributeValueRef id="Role=Operator"/></attributeValues>
                    <attributeValues><attributeValueRef id="Role=Admin"/></attributeValues>
                </attribute>
            </attributes>
        </account>

I'm at a loss as to how to get the multiple roles reflected in a single user entry.

Appreciate any help.

Share Improve this question asked Feb 15 at 14:39 surge3333surge3333 2074 silver badges9 bronze badges
Add a comment  | 

2 Answers 2

Reset to default 1
  • Consolidate the [pscustomobject] instances in your $data array by combining all instances that have the same .ID property value into a single instance whose .Role property is the aggregation of all roles, which you can do via the Group-Object cmdlet.

  • Operating on each consolidated instance, use $(...), the subexpression operator to embed code in your expandable here-string that iterates over potentially multiple roles and creates an XML element for each.

The following puts it all together; note that, for demonstration purposes, the BuildXml function outputs the generated XML to the display by default:

# Note the renaming of $Role to $Roles.
# Look for the $($Roles.ForEach({...})) lines below.
Function BuildXML {
  param($Type, $ID, $Roles, $Domain)
  $CC = '1234'
  $Account = "SNOW_CC$CC" + '_BNK0000_BLAH'

  if ($Type -eq 'N') {

    @”
       <account id="$Account">
       <name><![CDATA[$ID@$Domain]]></name>
           <endPoint>UNC</endPoint>
           <domain>UNC</domain>
           <comments/>
           <attributes>
               <attribute name="appUserName">
                   <attributeValues>
                       <attributeValue><value><![CDATA[$ID]]></value></attributeValue>
                   </attributeValues>
               </attribute>
               <attribute name="CostCentre">
                   <attributeValues>
                       <attributeValue><value><![CDATA[$CC]]></value></attributeValue>
                   </attributeValues>
               </attribute>
               <attribute name="Bank_Number">
                   <attributeValues>
                       <attributeValue><value><![CDATA[0000]]></value></attributeValue>
                   </attributeValues>
               </attribute>
               <attribute name="Directory">
                   <attributeValues>
                       <attributeValue><value><![CDATA[BLAH]]></value></attributeValue>
                   </attributeValues>
               </attribute>
               <attribute name="Role">
$($Roles.ForEach({
"                   <attributeValues><attributeValueRef id=`"Role=$_`"/></attributeValues>"
}) -join [Environment]::NewLine)
               </attribute>
           </attributes>
       </account>
"@ # | Add-Content $somePATH


  }
  elseif ($Type -eq 'P') {   

    @"
       <account id="$ID">
           <name><![CDATA[$ID@$Domain]]></name>
           <endPoint>ABC</endPoint>
           <domain>ABC</domain>
           <comments/>
           <attributes>
               <attribute name="AppBoRID">
                   <attributeValues>
                       <attributeValue><value><![CDATA[$ID]]></value></attributeValue>
                   </attributeValues>
               </attribute>
               <attribute name="Role">
$($Roles.ForEach({
"                   <attributeValues><attributeValueRef id=`"Role=$_`"/></attributeValues>"
}) -join [Environment]::NewLine)
               </attribute>
           </attributes>
       </account>
)               </attribute>
           </attributes>
       </account>
"@ # | Add-Content $WORKPATH$FEEDFILENAME
   
  }
} # End Function

#"# Your original sample data.
$data = @(
  [pscustomobject]@{Type = 'P'; ID = 'Fred'; Role = 'Operator'; Domain = 'Home' }
  [pscustomobject]@{Type = 'P'; ID = 'Fred'; Role = 'Admin'; Domain = 'Home' }
  [pscustomobject]@{Type = 'P'; ID = 'Wilma'; Role = 'Operator'; Domain = 'Home' }
  [pscustomobject]@{Type = 'P'; ID = 'Barney'; Role = 'Admin'; Domain = 'Work' }
  [pscustomobject]@{Type = 'N'; ID = 'Betty'; Role = 'Whatev'; Domain = 'Home' }
)

# Consolidate all instances that have the same .ID value into
# a single instance each that collects all .Role values, using
# Group-Object.
$consolidatedData = 
  $data | Group-Object Id | ForEach-Object {
    if ($_.Count -gt 1) {
      # Use *member-access enumeration* to collect the .Role property values
      # from all members of the group.
      $_.Group[0].Role = $_.Group.Role
    }
    $_.Group[0]
  }

# Build the XML for each potentially consolidated object.
foreach ($d in $consolidatedData) { BuildXML $d.Type $d.ID $d.Role $d.Domain }

Why not go for a result like this?

<attribute name="Role">
    <attributeValues>
        <attributeValueRef>"Operator"</attributeValueRef>
        <attributeValueRef>"Admin"</attributeValueRef>
    </attributeValues>
</attribute>

Anyway, since you are building the xml using Here-String templates, I would suggest:

Change the data by adding a single value or an array of values for the Role property like

$data = @(
    [pscustomobject]@{Type='P';ID='Fred';Role='Operator', 'Admin';Domain='Home'}
    ...
)

Then rewrite the function

function BuildXML {
    param(
        [char]$Type,
        [string]$ID,
        [string[]]$Role,   # string array or single value
        [string]$Domain
    )
    $CC = "1234"
    $Account = "SNOW_CC$CC" + "_BNK0000_BLAH"

# Change the templates by adding a placeholder text you can replace later
$typeN = @"
    <account id="$Account">
        <name><![CDATA[$ID@$Domain]]></name>
        <endPoint>UNC</endPoint>
        <domain>UNC</domain>
        <comments/>
        <attributes>
            <attribute name="appUserName">
                <attributeValues>
                    <attributeValue><value><![CDATA[$ID]]></value></attributeValue>
                </attributeValues>
            </attribute>
            <attribute name="CostCentre">
                <attributeValues>
                    <attributeValue><value><![CDATA[$CC]]></value></attributeValue>
                </attributeValues>
            </attribute>
            <attribute name="Bank_Number">
                <attributeValues>
                    <attributeValue><value><![CDATA[0000]]></value></attributeValue>
                </attributeValues>
            </attribute>
            <attribute name="Directory">
                <attributeValues>
                    <attributeValue><value><![CDATA[BLAH]]></value></attributeValue>
                </attributeValues>
            </attribute>
            <attribute name="Role">
                <attributeValues>
@@ROLES@@
                </attributeValues>
            </attribute>
        </attributes>
    </account>
"@

$typeP = @"
    <account id="$ID">
        <name><![CDATA[$ID@$Domain]]></name>
        <endPoint>ABC</endPoint>
        <domain>ABC</domain>
        <comments/>
        <attributes>
            <attribute name="AppBoRID">
                <attributeValues>
                    <attributeValue><value><![CDATA[$ID]]></value></attributeValue>
                </attributeValues>
            </attribute>
            <attribute name="Role">
                <attributeValues>
@@ROLES@@
                </attributeValues>
            </attribute>
        </attributes>
    </account>
"@

    if ($Type -eq "N") { $xmlTemplate = $typeN }
    else { $xmlTemplate = $typeP }

    # Replace the @@ROLES@@ placeholder
    # adjust this spacer to the number of tabs in your actual xml do it looks nice
    $spacer = "`t" * 5   # 5 TABS in this example
    $roleAttribs = $(foreach ($userRole in $Role) {
        '{0}<attributeValueRef id="Role={1}"/>' -f $spacer, $userRole
    }) -join [environment]::NewLine
    # save to file
    $xmlTemplate -replace '@@ROLES@@', $roleAttribs | Add-Content $WORKPATH$FEEDFILENAME
}

# for demo:
BuildXML -Type 'P' -ID 'Fred' -Role 'Admin', 'Operator' -Domain 'whatever'

The result would be

    <account id="Fred">
        <name><![CDATA[[email protected]]]></name>
        <endPoint>ABC</endPoint>
        <domain>ABC</domain>
        <comments/>
        <attributes>
            <attribute name="AppBoRID">
                <attributeValues>
                    <attributeValue><value><![CDATA[Fred]]></value></attributeValue>
                </attributeValues>
            </attribute>
            <attribute name="Role">
                <attributeValues>
                    <attributeValueRef id="Role=Admin"/>
                    <attributeValueRef id="Role=Operator"/>
                </attributeValues>
            </attribute>
        </attributes>
    </account>

I would also suggest you rename your function into something using Approved Verbs.
Perhaps Format-UserXml would be an idea?

发布评论

评论列表(0)

  1. 暂无评论