Alecbalec
Alecbalec

Reputation: 119

Powershell CSV to XML

So i'm attempting to convert a csv file to XML that contains 5 columns:

UserID;Cert;LastCertDate;ExpireDate;CertNumber
100001;oka;09.09.2018;09.09.2019;100001
100001;pik;10.10.2018;10.10.2019;200001

The XML structure should be as the following:

<Cv>
    <Owner>
        <UserID></UserID>
    </Owner>
    <Content>
        <Certificates>
            <Certificate>
                <Cert>oka</Cert>
                <LastCertDate>09.09.2018</LastCertDate>
                <ExpireDate>09.09.2019</ExpireDate>
                <CertNumber>100001</CertNumber>
            </Certificate>
            <Certificate>
                <Cert>pik</Cert>
                <LastCertDate>10.10.2018</LastCertDate>
                <ExpireDate>10.10.2019</ExpireDate>
                <CertNumber>200001</CertNumber>
            </Certificate>
        </Certificates>
    </Content>
</Cv>

Per now i only manage to get the 2nd Certificate into the XML since its on its on row in the CSV file.

Any idea on how i could merge the UserID and create a list that contains all the certificates on that specific user?

Thanks!

Upvotes: 2

Views: 7717

Answers (1)

mklement0
mklement0

Reputation: 439193

Bacon Bits gave the crucial pointer in a comment: Use Group-Object to group the custom objects created from your CSV rows via Import-Csv by user ID.

Here's a solution that uses string templating to provide the desired XML text output for each user, and writes that output to a file named fro the user ID:

# Per-user template.
# Note how a *literal* (single-quoted) here-string is used, so as to 
# *defer* expansion (interpolation) of the embedded expressions.
$docTemplate = @'
<Cv>
  <Owner>
      <UserID>$($cert.UserID)</UserID>
  </Owner>
  <Content>
      <Certificates>
$($certs -join "`n")
      </Certificates>
  </Content>
</Cv>
'@

# Per-certificate template.
$entryTemplate = @'
          <Certificate>
            <Cert>$($cert.Cert)</Cert>
            <LastCertDate>$($cert.LastCertDate)</LastCertDate>
            <ExpireDate>$($cert.ExpireDate)</ExpireDate>
            <CertNumber>$($cert.CertNumber)</CertNumber>
          </Certificate>
'@

Import-Csv file.csv -Delimiter ';' | Group-Object UserId -ov grp | ForEach-Object {
  # $_.Group contains all certificates associated with the user at hand.
  # Create an XML element for each certificate and collect the results
  # in array.
  $certs = foreach ($cert in $_.Group) {
    # Instantiate the per-certificate template.
    $ExecutionContext.InvokeCommand.ExpandString($entryTemplate)  
  }
  # Instantiate the per-user template, which encompasses
  # the per-certificate elements, and output the result.
  $ExecutionContext.InvokeCommand.ExpandString($docTemplate)
} | # Write the resulting XML document string to a file named for the user ID
  Set-Content -LiteralPath { $grp.Name + '.xml' }

Note how -ov grp (short for: -OutVariable grp) captures Group-Object's output in each pipeline iteration in variable $grp, so that the Set-Content call later in the pipeline can access $grp.Name - the user ID at hand - in the script block passed to Set-Content so as to create an output file named for that user ID (a technique known as delay-bind script blocks).

Note: Without an -Encoding argument, Windows PowerShell will use your system's "ANSI" character encoding (in PowerShell Core, you'd get BOM-less UTF-8).

Upvotes: 2

Related Questions