Skip to content

Commit 5c81e85

Browse files
Merge pull request #198 from gabriel-samfira/slight-cleanup
Slight cleanup
2 parents fad2e76 + dc20c75 commit 5c81e85

3 files changed

Lines changed: 99 additions & 53 deletions

File tree

0 Bytes
Binary file not shown.

powershell-yaml.psm1

Lines changed: 63 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,9 @@ function Convert-ValueToProperType {
123123
$intTypes = @([int], [long])
124124
if ([string]::IsNullOrEmpty($Node.Tag) -eq $false) {
125125
switch ($Node.Tag) {
126+
'!' {
127+
return $Node.Value
128+
}
126129
'tag:yaml.org,2002:str' {
127130
return $Node.Value
128131
}
@@ -310,6 +313,20 @@ function Convert-OrderedHashtableToDictionary {
310313
return $Data
311314
}
312315

316+
function Convert-GenericOrderedDictionaryToOrderedDictionary {
317+
param(
318+
[Parameter(Mandatory = $true, ValueFromPipeline = $true)]
319+
[System.Object]$Data
320+
)
321+
# Convert System.Collections.Generic ordered dictionaries to System.Collections.Specialized.OrderedDictionary
322+
# to preserve key order when serializing with YamlDotNet
323+
$ordered = [System.Collections.Specialized.OrderedDictionary]::new()
324+
foreach ($key in $Data.Keys) {
325+
$ordered[$key] = Convert-PSObjectToGenericObject $Data[$key]
326+
}
327+
return $ordered
328+
}
329+
313330
function Convert-ListToGenericList {
314331
param(
315332
[Parameter(Mandatory = $false, ValueFromPipeline = $true)]
@@ -333,13 +350,38 @@ function Convert-PSObjectToGenericObject {
333350
}
334351

335352
$dataType = $data.GetType()
353+
354+
# Check for OrderedDictionary types first (before generic IDictionary check)
336355
if (([System.Collections.Specialized.OrderedDictionary].IsAssignableFrom($dataType))) {
337356
return Convert-OrderedHashtableToDictionary $data
338-
} elseif (([System.Collections.IDictionary].IsAssignableFrom($dataType))) {
357+
}
358+
359+
# Check for System.Collections.Generic ordered dictionary types
360+
# These need to be converted to OrderedDictionary to preserve key order in YamlDotNet
361+
if ($dataType.IsGenericType) {
362+
$genericDef = $dataType.GetGenericTypeDefinition()
363+
$genericName = $genericDef.FullName
364+
365+
# Handle System.Collections.Generic.OrderedDictionary<K,V>
366+
if ($genericName -eq 'System.Collections.Generic.OrderedDictionary`2') {
367+
return Convert-GenericOrderedDictionaryToOrderedDictionary $data
368+
}
369+
370+
# Handle System.Collections.Generic.SortedDictionary<K,V>
371+
if ($genericName -eq 'System.Collections.Generic.SortedDictionary`2') {
372+
return Convert-GenericOrderedDictionaryToOrderedDictionary $data
373+
}
374+
}
375+
376+
# Generic IDictionary handling (for Hashtable, Dictionary, etc.)
377+
if (([System.Collections.IDictionary].IsAssignableFrom($dataType))) {
339378
return Convert-HashtableToDictionary $data
340-
} elseif (([System.Collections.IList].IsAssignableFrom($dataType))) {
379+
}
380+
381+
if (([System.Collections.IList].IsAssignableFrom($dataType))) {
341382
return Convert-ListToGenericList $data
342383
}
384+
343385
return $data
344386
}
345387

@@ -357,9 +399,7 @@ function ConvertFrom-Yaml {
357399
$d = ''
358400
}
359401
process {
360-
if ($Yaml -is [string]) {
361-
$d += $Yaml + "`n"
362-
}
402+
$d += $Yaml + "`n"
363403
}
364404

365405
end {
@@ -435,8 +475,7 @@ function ConvertTo-Yaml {
435475

436476
[Parameter(ParameterSetName = 'NoOptions')]
437477
[switch]$JsonCompatible,
438-
[switch]$UseFlowStyle,
439-
478+
440479
[switch]$KeepArray,
441480

442481
[switch]$Force
@@ -450,7 +489,7 @@ function ConvertTo-Yaml {
450489
}
451490
}
452491
end {
453-
if ($d -eq $null -or $d.Count -eq 0) {
492+
if ($null -eq $d -or $d.Count -eq 0) {
454493
return
455494
}
456495
if ($d.Count -eq 1 -and !($KeepArray)) {
@@ -465,11 +504,8 @@ function ConvertTo-Yaml {
465504
if ((Test-Path $OutFile) -and !$Force) {
466505
throw 'Target file already exists. Use -Force to overwrite.'
467506
}
468-
$wrt = New-Object 'System.IO.StreamWriter' $OutFile
469-
} else {
470-
$wrt = New-Object 'System.IO.StringWriter'
471507
}
472-
508+
473509
if ($PSCmdlet.ParameterSetName -eq 'NoOptions') {
474510
$Options = 0
475511
if ($JsonCompatible) {
@@ -478,18 +514,25 @@ function ConvertTo-Yaml {
478514
}
479515
}
480516

517+
if ($OutFile) {
518+
$wrt = New-Object 'System.IO.StreamWriter' $OutFile
519+
} else {
520+
$wrt = New-Object 'System.IO.StringWriter'
521+
}
522+
481523
try {
482524
$serializer = Get-Serializer $Options
483525
$serializer.Serialize($wrt, $norm)
484-
} catch {
485-
$_
526+
527+
if ($OutFile) {
528+
return
529+
} else {
530+
return $wrt.ToString()
531+
}
486532
} finally {
487-
$wrt.Close()
488-
}
489-
if ($OutFile) {
490-
return
491-
} else {
492-
return $wrt.ToString()
533+
if ($null -ne $wrt) {
534+
$wrt.Dispose()
535+
}
493536
}
494537
}
495538
}

src/PowerShellYamlSerializer.cs

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,26 @@ public override bool EnterMapping(IObjectDescriptor key, IObjectDescriptor value
3333
}
3434
}
3535

36+
internal static class PSObjectHelper {
37+
/// <summary>
38+
/// Unwraps a PSObject to its BaseObject if the BaseObject is not a PSCustomObject.
39+
/// </summary>
40+
/// <param name="obj">The object to potentially unwrap</param>
41+
/// <param name="unwrappedType">The type of the unwrapped object</param>
42+
/// <returns>The unwrapped object if it was a PSObject wrapping a non-PSCustomObject, otherwise the original object</returns>
43+
public static object UnwrapIfNeeded(object obj, out Type unwrappedType) {
44+
if (obj is PSObject psObj && psObj.BaseObject != null) {
45+
var baseType = psObj.BaseObject.GetType();
46+
if (baseType != typeof(System.Management.Automation.PSCustomObject)) {
47+
unwrappedType = baseType;
48+
return psObj.BaseObject;
49+
}
50+
}
51+
unwrappedType = obj?.GetType();
52+
return obj;
53+
}
54+
}
55+
3656
public class BigIntegerTypeConverter : IYamlTypeConverter {
3757
public bool Accepts(Type type) {
3858
return typeof(BigInteger).IsAssignableFrom(type);
@@ -76,26 +96,16 @@ public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerialize
7696
emitter.Emit(new MappingStart(AnchorName.Empty, TagName.Empty, true, mappingStyle));
7797
foreach (DictionaryEntry entry in hObj) {
7898
if(entry.Value == null) {
79-
if (this.omitNullValues == true) {
99+
if (this.omitNullValues) {
80100
continue;
81101
}
82102
serializer(entry.Key, entry.Key.GetType());
83103
emitter.Emit(new Scalar(AnchorName.Empty, "tag:yaml.org,2002:null", "", ScalarStyle.Plain, true, false));
84104
continue;
85105
}
86106
serializer(entry.Key, entry.Key.GetType());
87-
var objType = entry.Value.GetType();
88-
var val = entry.Value;
89-
if (entry.Value is PSObject nestedObj) {
90-
var nestedType = nestedObj.BaseObject.GetType();
91-
if (nestedType != typeof(System.Management.Automation.PSCustomObject)) {
92-
objType = nestedObj.BaseObject.GetType();
93-
val = nestedObj.BaseObject;
94-
}
95-
serializer(val, objType);
96-
} else {
97-
serializer(entry.Value, entry.Value.GetType());
98-
}
107+
var unwrapped = PSObjectHelper.UnwrapIfNeeded(entry.Value, out var unwrappedType);
108+
serializer(unwrapped, unwrappedType);
99109
}
100110
emitter.Emit(new MappingEnd());
101111
}
@@ -124,32 +134,25 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria
124134

125135
public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer) {
126136
var psObj = (PSObject)value;
127-
if (!typeof(IDictionary).IsAssignableFrom(psObj.BaseObject.GetType()) && !typeof(PSCustomObject).IsAssignableFrom(psObj.BaseObject.GetType())) {
137+
if (psObj.BaseObject != null &&
138+
!typeof(IDictionary).IsAssignableFrom(psObj.BaseObject.GetType()) &&
139+
!typeof(PSCustomObject).IsAssignableFrom(psObj.BaseObject.GetType())) {
128140
serializer(psObj.BaseObject, psObj.BaseObject.GetType());
129141
return;
130142
}
131143
var mappingStyle = this.useFlowStyle ? MappingStyle.Flow : MappingStyle.Block;
132144
emitter.Emit(new MappingStart(AnchorName.Empty, TagName.Empty, true, mappingStyle));
133145
foreach (var prop in psObj.Properties) {
134146
if (prop.Value == null) {
135-
if (this.omitNullValues == true) {
147+
if (this.omitNullValues) {
136148
continue;
137149
}
138150
serializer(prop.Name, prop.Name.GetType());
139151
emitter.Emit(new Scalar(AnchorName.Empty, "tag:yaml.org,2002:null", "", ScalarStyle.Plain, true, false));
140152
} else {
141153
serializer(prop.Name, prop.Name.GetType());
142-
var objType = prop.Value.GetType();
143-
var val = prop.Value;
144-
if (prop.Value is PSObject nestedPsObj) {
145-
var nestedType = nestedPsObj.BaseObject.GetType();
146-
if (nestedType != typeof(System.Management.Automation.PSCustomObject)) {
147-
objType = nestedPsObj.BaseObject.GetType();
148-
val = nestedPsObj.BaseObject;
149-
}
150-
}
151-
serializer(val, objType);
152-
154+
var unwrapped = PSObjectHelper.UnwrapIfNeeded(prop.Value, out var unwrappedType);
155+
serializer(unwrapped, unwrappedType);
153156
}
154157
}
155158
emitter.Emit(new MappingEnd());
@@ -197,7 +200,7 @@ public override void Emit(MappingStartEventInfo eventInfo, IEmitter emitter) {
197200

198201
public override void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter){
199202
eventInfo.Style = SequenceStyle.Flow;
200-
nextEmitter.Emit(eventInfo, emitter);
203+
base.Emit(eventInfo, emitter);
201204
}
202205
}
203206

@@ -206,19 +209,19 @@ public FlowStyleSequenceEmitter(IEventEmitter next): base(next) {}
206209

207210
public override void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter){
208211
eventInfo.Style = SequenceStyle.Flow;
209-
nextEmitter.Emit(eventInfo, emitter);
212+
base.Emit(eventInfo, emitter);
210213
}
211214
}
212215

213-
class BuilderUtils {
216+
public class BuilderUtils {
214217
public static SerializerBuilder BuildSerializer(
215218
SerializerBuilder builder,
216219
bool omitNullValues = false,
217220
bool useFlowStyle = false,
218221
bool useSequenceFlowStyle = false,
219222
bool jsonCompatible = false) {
220223

221-
if (jsonCompatible == true) {
224+
if (jsonCompatible) {
222225
useFlowStyle = true;
223226
useSequenceFlowStyle = true;
224227
}
@@ -228,14 +231,14 @@ public static SerializerBuilder BuildSerializer(
228231
.WithTypeConverter(new BigIntegerTypeConverter())
229232
.WithTypeConverter(new IDictionaryTypeConverter(omitNullValues, useFlowStyle))
230233
.WithTypeConverter(new PSObjectTypeConverter(omitNullValues, useFlowStyle));
231-
if (omitNullValues == true) {
234+
if (omitNullValues) {
232235
builder = builder
233236
.WithEmissionPhaseObjectGraphVisitor(args => new NullValueGraphVisitor(args.InnerVisitor));
234237
}
235-
if (useFlowStyle == true) {
238+
if (useFlowStyle) {
236239
builder = builder.WithEventEmitter(next => new FlowStyleAllEmitter(next));
237240
}
238-
if (useSequenceFlowStyle == true) {
241+
if (useSequenceFlowStyle) {
239242
builder = builder.WithEventEmitter(next => new FlowStyleSequenceEmitter(next));
240243
}
241244

0 commit comments

Comments
 (0)