Private/ConvertFrom-XML.ps1

<#

    .COPYRIGHT
    Copyright (c) Hugo Klemmestad. All rights reserved. Licensed under the MIT license.
    See https://github.com/ecitsolutions/Autotask/blob/master/LICENSE.md for license information.

#>


Function ConvertFrom-XML {
    <#
      .SYNOPSIS
      This function converts an array of XML elements to a custom psobject for easier parsing and coding.
      .DESCRIPTION
      The function an array of XML elements and recursively converts each element to a custom PowerShell
      object. Each XML property is converted to an object property with a value of datetime, double,
      integer or string.
      .INPUTS
      [System.XML.Xmlelement []]
      .OUTPUTS
      [System.Object[]]
      .EXAMPLE
      $element | ConvertFrom-XML
      Converts variable $element with must contain 1 or more Xml.Xmlelements to custom PSobjects with the
      same content.
      .NOTES
      NAME: ConvertFrom-XML
      
  #>

    [cmdletbinding()]
    Param
    (
        [Parameter(
            Mandatory = $true,
            ValueFromPipeLine = $true,
            ParameterSetName = 'Input_Object'
        )]
        [Xml.Xmlelement[]]
        $InputObject
    )

    begin {
 
        # Enable modern -Debug behavior
        if ($PSCmdlet.MyInvocation.BoundParameters['Debug'].IsPresent) { $DebugPreference = 'Continue' }
    
        Write-Debug ('{0}: Begin of function' -F $MyInvocation.MyCommand.Name)
        
        $result = @()

        # Set up TimeZone offset handling
        $timezoneid = if ($IsMacOS -or $IsLinux) { 'America/New_York' }
        else { 'Eastern Standard Time' }
        $EST = [System.Timezoneinfo]::FindSystemTimeZoneById($timezoneid)
    }
    process {
        
        foreach ($element in $InputObject) {
            # Get properties
            $properties = $element | Get-Member -MemberType property 
    
            # Create a new, empty object
            $object = New-Object -TypeName PSObject
    
            # Loop through all properties and add a member for each
            foreach ($property in $properties) {
                # We are accessing property values by dynamic naming. It is a lot easier
                # to reference dynamic property names with a string variable
                $propertyName = $property.Name
            
                # Extract/create a value based on the property definition string
                switch -Wildcard ($property.Definition) {
            
                    # Most properties are returned as strings. We will use a few tests to
                    # try to recognise other value types
                    'string*' {
                        # Test if it is a date first
                        if ($element.$propertyName -match '^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01]))') {
                            # Convert to a Datetime object in two steps to explicitly set datetime Kind to unspecified
                            # $d = [DateTime]$element.$propertyName
                            # $dateTime = New-Object DateTime $d.Year, $d.Month, $d.Day, $d.Hour, $d.Minute, $d.Second, ([DateTimeKind]::Unspecified)
                            [DateTime]$dateTime = $element.$propertyName

                            # Add timezone difference
                            $value = [TimeZoneInfo]::ConvertTime($dateTime, [TimeZoneInfo]::Local, $EST)
                        }
                        else {
                            # This isn't a date. We'll use a regex to avoid turning integers into doubles
                            switch -regex ($element.$propertyName) { 
                                '^\d+\.\d{4}$' { 
                                    [Double]$double = $element.$propertyName
                                    $value = $double
                                }
                    
                                '^\d+$' { 
                                    [Int64]$Integer = $element.$propertyName
                                    $value = $Integer
                                }
                                Default 
                                { $value = $element.$propertyName }
                            }
                        }
                        Add-Member -InputObject $object -MemberType Noteproperty -Name $propertyName -Value $value                                              
                    }
              
                    # For properties that are XML elements; recurse
                    'System.Xml.Xmlelement*' {
                        # A bit of recursive magic here...
                        $value = $element.$propertyName | ConvertFrom-XML
                        Add-Member -InputObject $object -MemberType Noteproperty -Name $propertyName -Value $value                              
                    }
              
                    # Arrays. Loop through elements and perform recursive magic
                    'System.Object*' {
                        $value = @()
                        foreach ($Item in $element.$propertyName) 
                        { $value += $Item | ConvertFrom-XML }
                        Add-Member -InputObject $object -MemberType Noteproperty -Name $propertyName -Value $value                              
                    }
              
                }
            }
        }

        $result += $object
    }

    end {
        Return $result
    }
}