What's a better way to decrease cardinality when serializing records I need to transfer data from one vendor's system to another.

The source vendor has a view that generates data that combines invoice and line-item data into records w/ line-item cardinality:

"Invoice","Date","Line","Description","Price"
"A","2/1/2020","1","something","100"
"A","2/1/2020","2","something else","75"
"B","1/1/2020","1","lorem ipsum","25"
"C","12/15/2020","1","foo bar","10.25"
"C","12/15/2020","2","foo bar","99.99"


The data needs to be transmitted, however, as a unit that combines the invoice and its line items:

&lt;I&gt;
  &lt;Invoice&gt;A&lt;/Invoice&gt;
  &lt;Date&gt;02/01/20&lt;/Date&gt;
  &lt;Items&gt;
    &lt;Item&gt;
      &lt;Line&gt;1&lt;/Line&gt;
      &lt;Description&gt;something&lt;/Description&gt;
      &lt;Price&gt;100&lt;/Price&gt;
    &lt;/Item&gt;
    &lt;Item&gt;
     &lt;Line&gt;2&lt;/Line&gt;
     &lt;Description&gt;something else&lt;/Description&gt;
     &lt;Price&gt;75&lt;/Price&gt;
    &lt;/Item&gt;
  &lt;/Items&gt;
&lt;/I&gt;


Towards this end, I've written the following:

# create testing data
function Get-Data
{
    [PSCustomObject]@{
        Invoice = 'A'
        Date='2/1/2020'
        Line=1
        Description = 'something'
        Price = 100.0
    }

    [PSCustomObject]@{
        Invoice = 'A'
        Date='2/1/2020'
        Line=2
        Description = 'something else'
        Price = 75.0
    }

    [PSCustomObject]@{
        Invoice = 'B'
        Date='1/1/2020'
        Line=1
        Description = 'lorem ipsum'
        Price = 25.0
    }
    [PSCustomObject]@{
        Invoice = 'C'
        Date='12/15/2020'
        Line=1
        Description = 'foo bar'
        Price = 10.25
    }
    [PSCustomObject]@{
        Invoice = 'C'
        Date='12/15/2020'
        Line=2
        Description = 'foo bar'
        Price = 99.99
    }

}

# serialize related line items
function ConvertTo-LineItemXml {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName)]
        [int]$Line,

        [Parameter(ValueFromPipelineByPropertyName)]
        [string]$Description,

        [Parameter(ValueFromPipelineByPropertyName)]
        [decimal]$Price
    )
    begin {}
    process
    {
        $xml += "&lt;Item&gt;&lt;Line&gt;$Line&lt;/Line&gt;&lt;Description&gt;$Description&lt;/Description&gt;&lt;Price&gt;$Price&lt;/Price&gt;&lt;/Item&gt;"
    }
    end
    {
        $xml
    }
}

# convert data to required Xml message and post
function Send-Invoice {
    [CmdletBinding()]
    param (
        [Parameter(ValueFromPipelineByPropertyName)]
        [string]$Invoice,

        [Parameter(ValueFromPipelineByPropertyName)]
        [datetime]$Date,

        [Parameter(ValueFromPipelineByPropertyName)]
        [pscustomobject]$Items
    )

    begin {}
    process {

        $xml = "&lt;I&gt;&lt;Invoice&gt;$Invoice&lt;/Invoice&gt;&lt;Date&gt;$($Date.ToString('MM/dd/yy'))&lt;/Date&gt;&lt;Items&gt;"

        $xml += $Items | ForEach-Object { 
            $_ | ConvertTo-LineItemXml 
        } 

        $xml += '&lt;/Items&gt;&lt;/I&gt;'

        Invoke-WebRequest -Method Post -Uri $Uri -Body $xml -ContentType 'application/xml'
    }
    end {}
}

# controller 
Get-Data | 
    # group by invoice
    Group-Object {$_.Invoice} |
        # for each group
        ForEach-Object {

            # get invoice-specific information from the group's first record
            $i = $_.Group | Select-Object -First 1 Invoice, Date

            # get the line-item-specific information from the group's records
            $li = $_.Group | Select-Object Line, Description, Price

            # assign to property
            $i | Add-Member -MemberType NoteProperty -Name 'Items' -Value $li

            # return structure
            $i 

        } | 
Send-Invoice # format as xml and post


I should probably move ConvertTo-LineItemXml from within Send-Invoice to allow the caller to have more control over the processing.

What other improvements can I make?
You can use XElement.
It is especially useful when constructing XML.

using namespace System.Xml.Linq
Add-Type -AssemblyName System.Xml.Linq

Get-Data | Group-Object Invoice | ForEach-Object {

    $items = $_.Group | ForEach-Object {
        [XElement]::new('Item', @(
            [XElement]::new('Line', $_.Line)
            [XElement]::new('Description', $_.Description)
            [XElement]::new('Price', $_.Price)))
    }

    $xml = [XElement]::new('I', @(
        [XElement]::new("Invoice", $_.Name)
        [XElement]::new("Date", $_.Group[0].Date)
        [XElement]::new('Items', $items)
    )).ToString()

    $xml
}