Skip to content

Commit

Permalink
Merge pull request #166 from gabriel-samfira/fix-json-compatibility
Browse files Browse the repository at this point in the history
Fix broken json compatibility
  • Loading branch information
gabriel-samfira authored Dec 16, 2024
2 parents 352d6b0 + 59300ff commit f100e2d
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 10 deletions.
93 changes: 93 additions & 0 deletions Tests/powershell-yaml.Tests.ps1
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,99 @@ Import-Module $modulePath
InModuleScope $moduleName {
$compareStrictly = Get-EquivalencyOption -Comparator Equality

Describe "Test flow styles" {
Context "Mappings, sequences and PSCustomObjects" {
It "Should serialize Block flow (default) correctly" {
$obj = [ordered]@{
aStringKey = "test"
anIntKey = 1
anArrayKey = @(1, 2, 3)
}
$expected = @"
aStringKey: test
anIntKey: 1
anArrayKey:
- 1
- 2
- 3
"@
$serialized = ConvertTo-Yaml $obj
Assert-Equivalent -Options $compareStrictly -Expected $expected -Actual $serialized

$pso = [pscustomobject]$obj
$serialized = ConvertTo-Yaml $pso
Assert-Equivalent -Options $compareStrictly -Expected $expected -Actual $serialized
}

It "Should serialize Flow flow correctly" {
$obj = [ordered]@{
aStringKey = "test"
anIntKey = 1
anArrayKey = @(1, 2, 3)
}
$expected = @"
{aStringKey: test, anIntKey: 1, anArrayKey: [1, 2, 3]}
"@
$serialized = ConvertTo-Yaml -Options UseFlowStyle $obj
Assert-Equivalent -Options $compareStrictly -Expected $expected -Actual $serialized

$pso = [pscustomobject]$obj
$serialized = ConvertTo-Yaml -Options UseFlowStyle $pso
Assert-Equivalent -Options $compareStrictly -Expected $expected -Actual $serialized
}

It "Should serialize SequenceFlowStyle correctly" {
$obj = [ordered]@{
aStringKey = "test"
anIntKey = 1
anArrayKey = @(1, 2, 3)
}
$expected = @"
aStringKey: test
anIntKey: 1
anArrayKey: [1, 2, 3]
"@
$serialized = ConvertTo-Yaml -Options UseSequenceFlowStyle $obj
Assert-Equivalent -Options $compareStrictly -Expected $expected -Actual $serialized

$pso = [pscustomobject]$obj
$serialized = ConvertTo-Yaml -Options UseSequenceFlowStyle $pso
Assert-Equivalent -Options $compareStrictly -Expected $expected -Actual $serialized
}

It "Should serialize JsonCompatible correctly" {
$obj = [ordered]@{
aStringKey = "test"
anIntKey = 1
anArrayKey = @(1, 2, 3)
}
$expected = @"
{"aStringKey": "test", "anIntKey": 1, "anArrayKey": [1, 2, 3]}
"@
$serialized = ConvertTo-Yaml -Options JsonCompatible $obj
Assert-Equivalent -Options $compareStrictly -Expected $expected -Actual $serialized

if ($PSVersionTable['PSEdition'] -eq 'Core') {
$deserializedWithJSonCommandlet = $serialized | ConvertFrom-Json -AsHashtable
Assert-Equivalent -Options $compareStrictly -Expected $obj -Actual $deserializedWithJSonCommandlet
}

$pso = [pscustomobject]$obj
$serialized = ConvertTo-Yaml -Options JsonCompatible $pso
Assert-Equivalent -Options $compareStrictly -Expected $expected -Actual $serialized

if ($PSVersionTable['PSEdition'] -eq 'Core') {
$deserializedWithJSonCommandlet = $serialized | ConvertFrom-Json -AsHashtable
Assert-Equivalent -Options $compareStrictly -Expected $obj -Actual $deserializedWithJSonCommandlet
}
}
}
}

Describe "Test PSCustomObject wrapped values are serialized correctly" {
Context "A PSCustomObject containing nested PSCustomObjects" {
It "Should serialize correctly" {
Expand Down
Binary file modified lib/net47/PowerShellYamlSerializer.dll
Binary file not shown.
Binary file modified lib/netstandard2.1/PowerShellYamlSerializer.dll
Binary file not shown.
7 changes: 4 additions & 3 deletions powershell-yaml.psm1
Original file line number Diff line number Diff line change
Expand Up @@ -392,7 +392,8 @@ function Get-Serializer {
)

$builder = $yamlDotNetAssembly.GetType("YamlDotNet.Serialization.SerializerBuilder")::new()

$JsonCompatible = $Options.HasFlag([SerializationOptions]::JsonCompatible)

if ($Options.HasFlag([SerializationOptions]::Roundtrip)) {
$builder = $builder.EnsureRoundtrip()
}
Expand All @@ -402,7 +403,7 @@ function Get-Serializer {
if ($Options.HasFlag([SerializationOptions]::EmitDefaults)) {
$builder = $builder.EmitDefaults()
}
if ($Options.HasFlag([SerializationOptions]::JsonCompatible)) {
if ($JsonCompatible) {
$builder = $builder.JsonCompatible()
}
if ($Options.HasFlag([SerializationOptions]::DefaultToStaticType)) {
Expand All @@ -418,7 +419,7 @@ function Get-Serializer {
$useSequenceFlowStyle = $Options.HasFlag([SerializationOptions]::UseSequenceFlowStyle)

$stringQuoted = $stringQuotedAssembly.GetType("BuilderUtils")
$builder = $stringQuoted::BuildSerializer($builder, $omitNull, $useFlowStyle, $useSequenceFlowStyle)
$builder = $stringQuoted::BuildSerializer($builder, $omitNull, $useFlowStyle, $useSequenceFlowStyle, $JsonCompatible)

return $builder.Build()
}
Expand Down
29 changes: 22 additions & 7 deletions src/PowerShellYamlSerializer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using YamlDotNet.Serialization;
using YamlDotNet.Serialization.EventEmitters;
using YamlDotNet.Core.Events;
using YamlDotNet.Serialization.NamingConventions;
using YamlDotNet.Serialization.ObjectGraphVisitors;

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

private bool omitNullValues;
private bool useFlowStyle;

public IDictionaryTypeConverter(bool omitNullValues = false) {
public IDictionaryTypeConverter(bool omitNullValues = false, bool useFlowStyle = false) {
this.omitNullValues = omitNullValues;
this.useFlowStyle = useFlowStyle;
}

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

public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer) {
var hObj = (IDictionary)value;
emitter.Emit(new MappingStart());
var mappingStyle = this.useFlowStyle ? MappingStyle.Flow : MappingStyle.Block;

emitter.Emit(new MappingStart(AnchorName.Empty, TagName.Empty, true, mappingStyle));
foreach (DictionaryEntry entry in hObj) {
if(entry.Value == null) {
if (this.omitNullValues == true) {
Expand All @@ -92,9 +97,11 @@ public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerialize
public class PSObjectTypeConverter : IYamlTypeConverter {

private bool omitNullValues;
private bool useFlowStyle;

public PSObjectTypeConverter(bool omitNullValues = false) {
public PSObjectTypeConverter(bool omitNullValues = false, bool useFlowStyle = false) {
this.omitNullValues = omitNullValues;
this.useFlowStyle = useFlowStyle;
}

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

public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerializer serializer) {
var psObj = (PSObject)value;
emitter.Emit(new MappingStart());
var mappingStyle = this.useFlowStyle ? MappingStyle.Flow : MappingStyle.Block;
emitter.Emit(new MappingStart(AnchorName.Empty, TagName.Empty, true, mappingStyle));
foreach (var prop in psObj.Properties) {
if (prop.Value == null) {
if (this.omitNullValues == true) {
Expand Down Expand Up @@ -196,12 +204,19 @@ public static SerializerBuilder BuildSerializer(
SerializerBuilder builder,
bool omitNullValues = false,
bool useFlowStyle = false,
bool useSequenceFlowStyle = false) {
bool useSequenceFlowStyle = false,
bool jsonCompatible = false) {

if (jsonCompatible == true) {
useFlowStyle = true;
useSequenceFlowStyle = true;
}

builder = builder
.WithEventEmitter(next => new StringQuotingEmitter(next))
.WithTypeConverter(new BigIntegerTypeConverter())
.WithTypeConverter(new IDictionaryTypeConverter(omitNullValues))
.WithTypeConverter(new PSObjectTypeConverter(omitNullValues));
.WithTypeConverter(new IDictionaryTypeConverter(omitNullValues, useFlowStyle))
.WithTypeConverter(new PSObjectTypeConverter(omitNullValues, useFlowStyle));
if (omitNullValues == true) {
builder = builder
.WithEmissionPhaseObjectGraphVisitor(args => new NullValueGraphVisitor(args.InnerVisitor));
Expand Down

0 comments on commit f100e2d

Please sign in to comment.