Functions/GenXdev.Helpers/ConvertTo-HashTable.cs

// ################################################################################
// Part of PowerShell module : GenXdev.Helpers
// Original cmdlet filename : ConvertTo-HashTable.cs
// Original author : René Vaessen / GenXdev
// Version : 3.28.2026
// ################################################################################
// Copyright (c) 2026 René Vaessen / GenXdev
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// ################################################################################



using System.Collections;
using System.Management.Automation;

namespace GenXdev.Helpers
{
    [System.ComponentModel.Description(@"
.SYNOPSIS
Converts a PSCustomObject to a HashTable recursively.
.DESCRIPTION
* This function converts a PSCustomObject and all its nested PSCustomObject
  properties into HashTables.
* It handles arrays and other collection types by processing each element
  recursively.
.EXAMPLE
```powershell
$object = [PSCustomObject]@{
    Name = ""John""
    Age = 30
    Details = [PSCustomObject]@{
        City = ""New York""
    }
}
$hashTable = ConvertTo-HashTable -InputObject $object
```

Convert a PSCustomObject to a HashTable with nested properties.
")]
    [Cmdlet(VerbsData.ConvertTo, "HashTable")]
    [OutputType(typeof(Hashtable), typeof(System.Collections.IEnumerable), typeof(System.ValueType), typeof(string))]
    public class ConvertToHashTableCommand : PSGenXdevCmdlet
    {
        /// <summary>
        /// The PSCustomObject to convert into a HashTable
        /// </summary>
        [Parameter(
            Mandatory = true,
            Position = 0,
            ValueFromPipeline = true,
            HelpMessage = "The PSCustomObject to convert into a HashTable"
        )]
        [ValidateNotNull]
        public object InputObject { get; set; }

        /// <summary>
        /// Begin processing - initialization logic
        /// </summary>
        protected override void BeginProcessing()
        {
            //WriteVerbose("Starting PSCustomObject to HashTable conversion");
        }

        /// <summary>
        /// Process record - main cmdlet logic
        /// </summary>
        protected override void ProcessRecord()
        {
            WriteObject(ConvertToHashTable(InputObject));
        }

        /// <summary>
        /// End processing - cleanup logic
        /// </summary>
        protected override void EndProcessing()
        {
            //WriteVerbose("Completed HashTable conversion");
        }

        private object ConvertToHashTable(object input)
        {

            if (input == null)
            {
                return new Hashtable(StringComparer.OrdinalIgnoreCase);
            }

            // handle simple value types directly
            if (input is System.ValueType || input is string)
            {
                return input;
            }

            // handle Hashtable
            if (input is System.Collections.Hashtable || (input is PSObject pso && pso.BaseObject is Hashtable))
            {
                // return empty hashtable if input is null
                if (input is PSObject psObject)
                {
                    input = psObject.BaseObject;
                }

                Hashtable hashTable = (Hashtable)input;

                System.Collections.Hashtable newTable = (System.Collections.Hashtable ) hashTable.Clone();
                foreach (var key in hashTable.Keys)
                {
                    newTable[key] = ConvertToHashTable(newTable[key]);
                }
                return newTable;
            }

            // handle PSCustomObject
            var psObj = input as PSObject;
            if (psObj != null)
            {
                var resultTable = new Dictionary<string, object>();

                // process each property of the current object
                foreach (var property in psObj.Properties)
                {
                    var value = property.Value;

                    // handle nested PSCustomObject properties
                    if (value is PSObject)
                    {
                        value = ConvertToHashTable(value);
                    }
                    // handle collection properties
                    else if (value is System.Collections.IEnumerable && !(value is string))
                    {
                        var list = new List<object>();
                        foreach (var element in (System.Collections.IEnumerable)value)
                        {
                            if (element is PSObject)
                            {
                                list.Add(ConvertToHashTable(element));
                            }
                            else
                            {
                                list.Add(element);
                            }
                        }
                        value = list.ToArray();
                    }
                    // handle simple value properties
                    else
                    {
                        // value remains as is
                    }

                    resultTable[property.Name] = value;
                }

                // create final hashtable with sorted keys
                var finalResultTable = new Hashtable(StringComparer.OrdinalIgnoreCase);
                foreach (var key in resultTable.Keys.OrderBy(k => k, StringComparer.Ordinal))
                {
                    finalResultTable[key] = resultTable[key];
                }

                return finalResultTable;
            }

            // handle collection types
            else if (input is System.Collections.IEnumerable && !(input is string))
            {
                var list = new List<object>();
                foreach (var element in (System.Collections.IEnumerable)input)
                {
                    list.Add(ConvertToHashTable(element));
                }

                return list.ToArray();
            }

            // handle other types
            else
            {
                return input;
            }
        }
    }
}