Skip to content

Commit 31abc84

Browse files
Fix broken json compatibility
The IDictionary and PSCustomObject type converters have to emit a MAppingStart and MappingEnd. When we added these calls, we ommited to properly set the flow style for these markers, which brije JSON compatibility. This change adds the proper options to those Emit calls and adds tests to ensure Flow styles are obeyed. Signed-off-by: Gabriel Adrian Samfira <[email protected]>
1 parent 352d6b0 commit 31abc84

File tree

5 files changed

+115
-10
lines changed

5 files changed

+115
-10
lines changed

Tests/powershell-yaml.Tests.ps1

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,95 @@ Import-Module $modulePath
2828
InModuleScope $moduleName {
2929
$compareStrictly = Get-EquivalencyOption -Comparator Equality
3030

31+
Describe "Test flow styles" {
32+
Context "Mappings, sequences and PSCustomObjects" {
33+
It "Should serialize Block flow (default) correctly" {
34+
$obj = [ordered]@{
35+
aStringKey = "test"
36+
anIntKey = 1
37+
anArrayKey = @(1, 2, 3)
38+
}
39+
$expected = @"
40+
aStringKey: test
41+
anIntKey: 1
42+
anArrayKey:
43+
- 1
44+
- 2
45+
- 3
46+
47+
"@
48+
$serialized = ConvertTo-Yaml $obj
49+
Assert-Equivalent -Options $compareStrictly -Expected $expected -Actual $serialized
50+
51+
$pso = [pscustomobject]$obj
52+
$serialized = ConvertTo-Yaml $pso
53+
Assert-Equivalent -Options $compareStrictly -Expected $expected -Actual $serialized
54+
}
55+
56+
It "Should serialize Flow flow correctly" {
57+
$obj = [ordered]@{
58+
aStringKey = "test"
59+
anIntKey = 1
60+
anArrayKey = @(1, 2, 3)
61+
}
62+
$expected = @"
63+
{aStringKey: test, anIntKey: 1, anArrayKey: [1, 2, 3]}
64+
65+
"@
66+
$serialized = ConvertTo-Yaml -Options UseFlowStyle $obj
67+
Assert-Equivalent -Options $compareStrictly -Expected $expected -Actual $serialized
68+
69+
$pso = [pscustomobject]$obj
70+
$serialized = ConvertTo-Yaml -Options UseFlowStyle $pso
71+
Assert-Equivalent -Options $compareStrictly -Expected $expected -Actual $serialized
72+
}
73+
74+
It "Should serialize SequenceFlowStyle correctly" {
75+
$obj = [ordered]@{
76+
aStringKey = "test"
77+
anIntKey = 1
78+
anArrayKey = @(1, 2, 3)
79+
}
80+
$expected = @"
81+
aStringKey: test
82+
anIntKey: 1
83+
anArrayKey: [1, 2, 3]
84+
85+
"@
86+
$serialized = ConvertTo-Yaml -Options UseSequenceFlowStyle $obj
87+
Assert-Equivalent -Options $compareStrictly -Expected $expected -Actual $serialized
88+
89+
$pso = [pscustomobject]$obj
90+
$serialized = ConvertTo-Yaml -Options UseSequenceFlowStyle $pso
91+
Assert-Equivalent -Options $compareStrictly -Expected $expected -Actual $serialized
92+
}
93+
94+
It "Should serialize JsonCompatible correctly" {
95+
$obj = [ordered]@{
96+
aStringKey = "test"
97+
anIntKey = 1
98+
anArrayKey = @(1, 2, 3)
99+
}
100+
$expected = @"
101+
{"aStringKey": "test", "anIntKey": 1, "anArrayKey": [1, 2, 3]}
102+
103+
"@
104+
$serialized = ConvertTo-Yaml -Options JsonCompatible $obj
105+
Assert-Equivalent -Options $compareStrictly -Expected $expected -Actual $serialized
106+
107+
$deserializedWithJSonCommandlet = $serialized | ConvertFrom-Json -AsHashtable
108+
Assert-Equivalent -Options $compareStrictly -Expected $obj -Actual $deserializedWithJSonCommandlet
109+
110+
$pso = [pscustomobject]$obj
111+
$serialized = ConvertTo-Yaml -Options JsonCompatible $pso
112+
Assert-Equivalent -Options $compareStrictly -Expected $expected -Actual $serialized
113+
114+
$deserializedWithJSonCommandlet = $serialized | ConvertFrom-Json -AsHashtable
115+
Assert-Equivalent -Options $compareStrictly -Expected $obj -Actual $deserializedWithJSonCommandlet
116+
}
117+
}
118+
}
119+
31120
Describe "Test PSCustomObject wrapped values are serialized correctly" {
32121
Context "A PSCustomObject containing nested PSCustomObjects" {
33122
It "Should serialize correctly" {
512 Bytes
Binary file not shown.
512 Bytes
Binary file not shown.

powershell-yaml.psm1

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -392,7 +392,8 @@ function Get-Serializer {
392392
)
393393

394394
$builder = $yamlDotNetAssembly.GetType("YamlDotNet.Serialization.SerializerBuilder")::new()
395-
395+
$JsonCompatible = $Options.HasFlag([SerializationOptions]::JsonCompatible)
396+
396397
if ($Options.HasFlag([SerializationOptions]::Roundtrip)) {
397398
$builder = $builder.EnsureRoundtrip()
398399
}
@@ -402,7 +403,7 @@ function Get-Serializer {
402403
if ($Options.HasFlag([SerializationOptions]::EmitDefaults)) {
403404
$builder = $builder.EmitDefaults()
404405
}
405-
if ($Options.HasFlag([SerializationOptions]::JsonCompatible)) {
406+
if ($JsonCompatible) {
406407
$builder = $builder.JsonCompatible()
407408
}
408409
if ($Options.HasFlag([SerializationOptions]::DefaultToStaticType)) {
@@ -418,7 +419,7 @@ function Get-Serializer {
418419
$useSequenceFlowStyle = $Options.HasFlag([SerializationOptions]::UseSequenceFlowStyle)
419420

420421
$stringQuoted = $stringQuotedAssembly.GetType("BuilderUtils")
421-
$builder = $stringQuoted::BuildSerializer($builder, $omitNull, $useFlowStyle, $useSequenceFlowStyle)
422+
$builder = $stringQuoted::BuildSerializer($builder, $omitNull, $useFlowStyle, $useSequenceFlowStyle, $JsonCompatible)
422423

423424
return $builder.Build()
424425
}

src/PowerShellYamlSerializer.cs

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
using YamlDotNet.Serialization;
99
using YamlDotNet.Serialization.EventEmitters;
1010
using YamlDotNet.Core.Events;
11+
using YamlDotNet.Serialization.NamingConventions;
1112
using YamlDotNet.Serialization.ObjectGraphVisitors;
1213

1314
public sealed class NullValueGraphVisitor : ChainedObjectGraphVisitor
@@ -52,9 +53,11 @@ public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerialize
5253
public class IDictionaryTypeConverter : IYamlTypeConverter {
5354

5455
private bool omitNullValues;
56+
private bool useFlowStyle;
5557

56-
public IDictionaryTypeConverter(bool omitNullValues = false) {
58+
public IDictionaryTypeConverter(bool omitNullValues = false, bool useFlowStyle = false) {
5759
this.omitNullValues = omitNullValues;
60+
this.useFlowStyle = useFlowStyle;
5861
}
5962

6063
public bool Accepts(Type type) {
@@ -68,7 +71,9 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria
6871

6972
public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer) {
7073
var hObj = (IDictionary)value;
71-
emitter.Emit(new MappingStart());
74+
var mappingStyle = this.useFlowStyle ? MappingStyle.Flow : MappingStyle.Block;
75+
76+
emitter.Emit(new MappingStart(AnchorName.Empty, TagName.Empty, true, mappingStyle));
7277
foreach (DictionaryEntry entry in hObj) {
7378
if(entry.Value == null) {
7479
if (this.omitNullValues == true) {
@@ -92,9 +97,11 @@ public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerialize
9297
public class PSObjectTypeConverter : IYamlTypeConverter {
9398

9499
private bool omitNullValues;
100+
private bool useFlowStyle;
95101

96-
public PSObjectTypeConverter(bool omitNullValues = false) {
102+
public PSObjectTypeConverter(bool omitNullValues = false, bool useFlowStyle = false) {
97103
this.omitNullValues = omitNullValues;
104+
this.useFlowStyle = useFlowStyle;
98105
}
99106

100107
public bool Accepts(Type type) {
@@ -110,7 +117,8 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria
110117

111118
public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer) {
112119
var psObj = (PSObject)value;
113-
emitter.Emit(new MappingStart());
120+
var mappingStyle = this.useFlowStyle ? MappingStyle.Flow : MappingStyle.Block;
121+
emitter.Emit(new MappingStart(AnchorName.Empty, TagName.Empty, true, mappingStyle));
114122
foreach (var prop in psObj.Properties) {
115123
if (prop.Value == null) {
116124
if (this.omitNullValues == true) {
@@ -196,12 +204,19 @@ public static SerializerBuilder BuildSerializer(
196204
SerializerBuilder builder,
197205
bool omitNullValues = false,
198206
bool useFlowStyle = false,
199-
bool useSequenceFlowStyle = false) {
207+
bool useSequenceFlowStyle = false,
208+
bool jsonCompatible = false) {
209+
210+
if (jsonCompatible == true) {
211+
useFlowStyle = true;
212+
useSequenceFlowStyle = true;
213+
}
214+
200215
builder = builder
201216
.WithEventEmitter(next => new StringQuotingEmitter(next))
202217
.WithTypeConverter(new BigIntegerTypeConverter())
203-
.WithTypeConverter(new IDictionaryTypeConverter(omitNullValues))
204-
.WithTypeConverter(new PSObjectTypeConverter(omitNullValues));
218+
.WithTypeConverter(new IDictionaryTypeConverter(omitNullValues, useFlowStyle))
219+
.WithTypeConverter(new PSObjectTypeConverter(omitNullValues, useFlowStyle));
205220
if (omitNullValues == true) {
206221
builder = builder
207222
.WithEmissionPhaseObjectGraphVisitor(args => new NullValueGraphVisitor(args.InnerVisitor));

0 commit comments

Comments
 (0)