Private/ConvertFrom-XML.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
<#
 
    .COPYRIGHT
    Copyright (c) ECIT Solutions AS. 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
    }
}