From f95876c10bc693a6e17dc74018d1727dfd6625ed Mon Sep 17 00:00:00 2001 From: Jordan Borean Date: Mon, 15 Jan 2024 12:28:15 +1000 Subject: [PATCH] Better support for array serialization Ensure that any IList type like ArrayList or a Generic list is treated as a TOML array to align with how ConvertTo-Json works. Also properly deserializes a TOML array of array/table values into actual dotnet objects to provide a better deserialized value. --- CHANGELOG.md | 6 +++ module/PSToml.psd1 | 2 +- src/PSToml/ConvertFromToml.cs | 13 ++++++- src/PSToml/ConvertToToml.cs | 49 +++++++++++++++++++++--- tests/ConvertFrom-Toml.Tests.ps1 | 66 ++++++++++++++++++++++++++++++++ tests/ConvertTo-Toml.Tests.ps1 | 20 ++++++++++ 6 files changed, 148 insertions(+), 8 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 05163ec..6b6a63a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,11 @@ # Changelog for PSToml +## v0.3.1 - TBD + ++ Serialize any IList type as a Toml array value and not just an array ++ Deserialize Toml array values that contain table/array/ values into the proper dotnet object ++ Support serializing `IntPtr` and `UIntPtr` instances + ## v0.3.0 - 2023-11-28 + Migrated to new ALC structure that simplifies the code and ensures deps are loaded in the ALC diff --git a/module/PSToml.psd1 b/module/PSToml.psd1 index e7a008f..c592cfa 100644 --- a/module/PSToml.psd1 +++ b/module/PSToml.psd1 @@ -14,7 +14,7 @@ RootModule = 'PSToml.psm1' # Version number of this module. - ModuleVersion = '0.3.0' + ModuleVersion = '0.3.1' # Supported PSEditions # CompatiblePSEditions = @() diff --git a/src/PSToml/ConvertFromToml.cs b/src/PSToml/ConvertFromToml.cs index acb12dc..5797588 100644 --- a/src/PSToml/ConvertFromToml.cs +++ b/src/PSToml/ConvertFromToml.cs @@ -75,7 +75,18 @@ private OrderedDictionary ConvertToOrderedDictionary(TomlTable table) private object?[] ConvertToArray(TomlArray array) { List result = new(); - result.AddRange(array); + foreach (object? value in array) + { + object? newValue = value switch + { + TomlArray a => ConvertToArray(a), + TomlTable t => ConvertToOrderedDictionary(t), + TomlTableArray ta => ConvertToListOfOrderedDictionary(ta), + TomlDateTime dt => dt.DateTime, + _ => value, + }; + result.Add(newValue); + } return result.ToArray(); } diff --git a/src/PSToml/ConvertToToml.cs b/src/PSToml/ConvertToToml.cs index 8022c96..f54ba59 100644 --- a/src/PSToml/ConvertToToml.cs +++ b/src/PSToml/ConvertToToml.cs @@ -1,5 +1,6 @@ using System; using System.Collections; +using System.Collections.Generic; using System.Management.Automation; using Tomlyn; using Tomlyn.Model; @@ -93,26 +94,54 @@ private object ConvertToTomlObject(object? inputObject, int depth) return inputObject switch { IDictionary dict => ConvertToTomlTable(dict, depth), - Array array => ConvertToTomlArray(array, depth), + IList array => ConvertToTomlArray(array, depth), _ => ConvertToTomlFriendlyObject(inputObject, depth), }; } - private TomlArray ConvertToTomlArray(Array array, int depth) + private TomlObject ConvertToTomlArray(IList array, int depth) { - TomlArray result = new(); + List results = new(); + bool isTableArray = true; foreach (object value in array) { - result.Add(ConvertToTomlObject(value, depth - 1)); + object toSerialize = ConvertToTomlObject(value, depth - 1); + + if (!(toSerialize is TomlTable tt)) + { + isTableArray = false; + } + + results.Add(toSerialize); } - return result; + if (isTableArray) + { + TomlTableArray ta = new(); + foreach (object v in results) + { + ta.Add((TomlTable)v); + } + + return ta; + } + else + { + TomlArray a = new(); + foreach (object v in results) + { + a.Add(v); + } + + return a; + + } } private TomlTable ConvertToTomlTable(IDictionary dict, int depth) { - TomlTable model = new(); + TomlTable model = new(inline: false); foreach (DictionaryEntry entry in dict) { object value = ConvertToTomlObject(entry.Value ?? "", depth - 1); @@ -141,6 +170,14 @@ private object ConvertToTomlFriendlyObject(object obj, int depth) { return Convert.ChangeType(enumObj, enumObj.GetTypeCode()); } + else if (obj is IntPtr ptr) + { + return (long)ptr; + } + else if (obj is UIntPtr uptr) + { + return (ulong)uptr; + } if ( obj is bool || diff --git a/tests/ConvertFrom-Toml.Tests.ps1 b/tests/ConvertFrom-Toml.Tests.ps1 index 142a6d4..87bbb54 100644 --- a/tests/ConvertFrom-Toml.Tests.ps1 +++ b/tests/ConvertFrom-Toml.Tests.ps1 @@ -149,4 +149,70 @@ color = "gray" $actual.products[2].sku | Should -Be 284758393 $actual.products[2].color | Should -Be gray } + + It "Converts inline array of normal values" { + $actual = ConvertFrom-Toml -InputObject @' +foo = [1, "2", 3.4] +'@ + + ,$actual.foo | Should -BeOfType ([object[]]) + $actual.foo.Count | Should -Be 3 + + $actual.foo[0] | Should -Be 1 + $actual.foo[0] | Should -BeOfType ([long]) + + $actual.foo[1] | Should -Be 2 + $actual.foo[1] | Should -BeOfType ([string]) + + $actual.foo[2] | Should -Be 3.4 + $actual.foo[2] | Should -BeOfType ([double]) + } + + It "Converts inline array of array values" { + $actual = ConvertFrom-Toml -InputObject @' +foo = [[1, 2], ["3", "4"], [5.6]] +'@ + + ,$actual.foo | Should -BeOfType ([object[]]) + $actual.foo.Count | Should -Be 3 + + + ,$actual.foo[0] | Should -BeOfType ([object[]]) + $actual.foo[0].Count | Should -Be 2 + $actual.foo[0][0] | Should -Be 1 + $actual.foo[0][0] | Should -BeOfType ([long]) + $actual.foo[0][1] | Should -Be 2 + $actual.foo[0][1] | Should -BeOfType ([long]) + + ,$actual.foo[1] | Should -BeOfType ([object[]]) + $actual.foo[1].Count | Should -Be 2 + $actual.foo[1][0] | Should -Be 3 + $actual.foo[1][0] | Should -BeOfType ([string]) + $actual.foo[1][1] | Should -Be 4 + $actual.foo[1][1] | Should -BeOfType ([string]) + + ,$actual.foo[2] | Should -BeOfType ([object[]]) + $actual.foo[2].Count | Should -Be 1 + $actual.foo[2][0] | Should -Be 5.6 + $actual.foo[2][0] | Should -BeOfType ([double]) + } + + It "Converts inline array of table values" { + $actual = ConvertFrom-Toml -InputObject @' +foo = [{foo = 1, bar = 2}, {foo = 3, bar = 4}] +'@ + + ,$actual.foo | Should -BeOfType ([object[]]) + $actual.foo.Count | Should -Be 2 + + $actual.foo[0] | Should -BeOfType ([System.Collections.Specialized.OrderedDictionary]) + $actual.foo[0].Keys.Count | Should -Be 2 + $actual.foo[0].foo | Should -Be 1 + $actual.foo[0].bar | Should -Be 2 + + $actual.foo[1] | Should -BeOfType ([System.Collections.Specialized.OrderedDictionary]) + $actual.foo[1].Keys.Count | Should -Be 2 + $actual.foo[1].foo | Should -Be 3 + $actual.foo[1].bar | Should -Be 4 + } } diff --git a/tests/ConvertTo-Toml.Tests.ps1 b/tests/ConvertTo-Toml.Tests.ps1 index 74d8fea..e8b3c0e 100644 --- a/tests/ConvertTo-Toml.Tests.ps1 +++ b/tests/ConvertTo-Toml.Tests.ps1 @@ -78,12 +78,32 @@ name = "plantain" char = [char]'c' null = $null enum = [System.IO.FileShare]::ReadWrite + intptr = [IntPtr]::new(-1) + uintptr = [UIntPtr]::new(1) }) $actual | Should -Be @' guid = "00000000-0000-0000-0000-000000000000" char = "c" null = "" enum = 3 +intptr = -1 +uintptr = 1 + +'@ + } + + It "Serializes array and list types" { + $actual = ConvertTo-Toml -InputObject ([Ordered]@{ + array = @(1, 2, 3) + typed_array = [string[]]@(1, 2, "3") + array_list = [System.Collections.ArrayList]@(1, 2, 3) + generic_list = [System.Collections.Generic.List[string]]@(1, "2", 3) + }) + $actual | Should -Be @' +array = [1, 2, 3] +typed_array = ["1", "2", "3"] +array_list = [1, 2, 3] +generic_list = ["1", "2", "3"] '@ }