From b22f5ba8f55ff66ce0ef6b584eec861b0305ffcc Mon Sep 17 00:00:00 2001 From: Adeel Date: Sun, 10 May 2020 23:41:40 +0300 Subject: [PATCH] Add anchor/alias support in JsonEventEmitter --- .../Serialization/SerializationTests.cs | 23 ++++++++++-- YamlDotNet/Core/Emitter.cs | 4 +- YamlDotNet/Core/EmitterSettings.cs | 37 ++++++++++++++++++- .../EventEmitters/JsonEventEmitter.cs | 5 +-- YamlDotNet/Serialization/EventInfo.cs | 3 +- .../AnchorAssigningObjectGraphVisitor.cs | 7 ++-- YamlDotNet/Serialization/SerializerBuilder.cs | 8 ++-- .../{YamlMember.cs => YamlMemberAttribute.cs} | 0 8 files changed, 71 insertions(+), 16 deletions(-) rename YamlDotNet/Serialization/{YamlMember.cs => YamlMemberAttribute.cs} (100%) diff --git a/YamlDotNet.Test/Serialization/SerializationTests.cs b/YamlDotNet.Test/Serialization/SerializationTests.cs index 823c841a4..8be47d7ef 100644 --- a/YamlDotNet.Test/Serialization/SerializationTests.cs +++ b/YamlDotNet.Test/Serialization/SerializationTests.cs @@ -27,19 +27,17 @@ using System.ComponentModel; using System.Drawing; using System.Dynamic; +using System.Globalization; using System.IO; using System.Linq; -using System.Globalization; using System.Reflection; using System.Text.RegularExpressions; using Xunit; using YamlDotNet.Core; using YamlDotNet.Core.Events; -using YamlDotNet.RepresentationModel; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; using YamlDotNet.Serialization.ObjectFactories; -using YamlDotNet.Serialization.TypeInspectors; namespace YamlDotNet.Test.Serialization { @@ -810,6 +808,25 @@ public void SerializationOfLongKeysWorksInJson() writer.ToString().Should().NotContain("?"); } + [Fact] + public void SerializationOfAnchorWorksInJson() + { + var deserializer = new DeserializerBuilder().Build(); + var yamlObject = deserializer.Deserialize(Yaml.ReaderForText(@" +x: &anchor1 + z: + v: 1 +y: + k: *anchor1")); + + var serializer = new SerializerBuilder() + .JsonCompatible() + .Build(); + + serializer.Serialize(yamlObject).Trim().Should() + .BeEquivalentTo(@"{""x"": {""z"": {""v"": ""1""}}, ""y"": {""k"": {""z"": {""v"": ""1""}}}}"); + } + [Fact] // Todo: this is actually roundtrip public void DeserializationOfDefaultsWorkInJson() diff --git a/YamlDotNet/Core/Emitter.cs b/YamlDotNet/Core/Emitter.cs index bb314169b..9364f837f 100644 --- a/YamlDotNet/Core/Emitter.cs +++ b/YamlDotNet/Core/Emitter.cs @@ -45,6 +45,7 @@ public class Emitter : IEmitter private readonly int maxSimpleKeyLength; private readonly bool isCanonical; + private readonly bool skipAnchorName; private readonly int bestIndent; private readonly int bestWidth; private EmitterState state; @@ -140,6 +141,7 @@ public Emitter(TextWriter output, EmitterSettings settings) this.bestWidth = settings.BestWidth; this.isCanonical = settings.IsCanonical; this.maxSimpleKeyLength = settings.MaxSimpleKeyLength; + this.skipAnchorName = settings.SkipAnchorName; this.output = output; this.outputUsesUnicodeEncoding = IsUnicode(output.Encoding); @@ -1375,7 +1377,7 @@ private void EmitMappingStart(ParsingEvent evt) private void ProcessAnchor() { - if (anchorData.anchor != null) + if (anchorData.anchor != null && !skipAnchorName) { WriteIndicator(anchorData.isAlias ? "*" : "&", true, false, false); WriteAnchor(anchorData.anchor); diff --git a/YamlDotNet/Core/EmitterSettings.cs b/YamlDotNet/Core/EmitterSettings.cs index 3451dd13b..65a638e33 100644 --- a/YamlDotNet/Core/EmitterSettings.cs +++ b/YamlDotNet/Core/EmitterSettings.cs @@ -1,4 +1,25 @@ -using System; +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors + +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: + +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. + +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; namespace YamlDotNet.Core { @@ -19,6 +40,11 @@ public sealed class EmitterSettings /// public bool IsCanonical { get; } = false; + /// + /// If true, write output without anchor names. + /// + public bool SkipAnchorName { get; private set; } + /// /// The maximum allowed length for simple keys. /// @@ -33,7 +59,7 @@ public EmitterSettings() { } - public EmitterSettings(int bestIndent, int bestWidth, bool isCanonical, int maxSimpleKeyLength) + public EmitterSettings(int bestIndent, int bestWidth, bool isCanonical, int maxSimpleKeyLength, bool skipAnchorName = false) { if (bestIndent < 2 || bestIndent > 9) { @@ -54,6 +80,7 @@ public EmitterSettings(int bestIndent, int bestWidth, bool isCanonical, int maxS BestWidth = bestWidth; IsCanonical = isCanonical; MaxSimpleKeyLength = maxSimpleKeyLength; + SkipAnchorName = skipAnchorName; } public EmitterSettings WithBestIndent(int bestIndent) @@ -95,5 +122,11 @@ public EmitterSettings Canonical() MaxSimpleKeyLength ); } + + public EmitterSettings WithoutAnchorName() + { + SkipAnchorName = true; + return this; + } } } diff --git a/YamlDotNet/Serialization/EventEmitters/JsonEventEmitter.cs b/YamlDotNet/Serialization/EventEmitters/JsonEventEmitter.cs index 3334821d5..31bb0d2b1 100644 --- a/YamlDotNet/Serialization/EventEmitters/JsonEventEmitter.cs +++ b/YamlDotNet/Serialization/EventEmitters/JsonEventEmitter.cs @@ -20,7 +20,6 @@ // SOFTWARE. using System; -using System.Globalization; using YamlDotNet.Core; using YamlDotNet.Core.Events; @@ -35,7 +34,7 @@ public JsonEventEmitter(IEventEmitter nextEmitter) public override void Emit(AliasEventInfo eventInfo, IEmitter emitter) { - throw new NotSupportedException("Aliases are not supported in JSON"); + eventInfo.NeedsExpansion = true; } public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) @@ -124,4 +123,4 @@ public override void Emit(SequenceStartEventInfo eventInfo, IEmitter emitter) base.Emit(eventInfo, emitter); } } -} \ No newline at end of file +} diff --git a/YamlDotNet/Serialization/EventInfo.cs b/YamlDotNet/Serialization/EventInfo.cs index b1632a40c..ffd19b245 100644 --- a/YamlDotNet/Serialization/EventInfo.cs +++ b/YamlDotNet/Serialization/EventInfo.cs @@ -44,6 +44,7 @@ public AliasEventInfo(IObjectDescriptor source, string alias) } public string Alias { get; } + public bool NeedsExpansion { get; set; } } public class ObjectEventInfo : EventInfo @@ -109,4 +110,4 @@ public SequenceEndEventInfo(IObjectDescriptor source) { } } -} \ No newline at end of file +} diff --git a/YamlDotNet/Serialization/ObjectGraphVisitors/AnchorAssigningObjectGraphVisitor.cs b/YamlDotNet/Serialization/ObjectGraphVisitors/AnchorAssigningObjectGraphVisitor.cs index 3f7065d59..fd5e663d7 100644 --- a/YamlDotNet/Serialization/ObjectGraphVisitors/AnchorAssigningObjectGraphVisitor.cs +++ b/YamlDotNet/Serialization/ObjectGraphVisitors/AnchorAssigningObjectGraphVisitor.cs @@ -45,8 +45,9 @@ public override bool Enter(IObjectDescriptor value, IEmitter context) var alias = aliasProvider.GetAlias(value.Value); if (alias != null && !emittedAliases.Add(alias)) { - eventEmitter.Emit(new AliasEventInfo(value, alias), context); - return false; + var aliasEventInfo = new AliasEventInfo(value, alias); + eventEmitter.Emit(aliasEventInfo, context); + return aliasEventInfo.NeedsExpansion; } } return base.Enter(value, context); @@ -74,4 +75,4 @@ public override void VisitScalar(IObjectDescriptor scalar, IEmitter context) eventEmitter.Emit(scalarInfo, context); } } -} \ No newline at end of file +} diff --git a/YamlDotNet/Serialization/SerializerBuilder.cs b/YamlDotNet/Serialization/SerializerBuilder.cs index 0cd349454..ace2b181a 100755 --- a/YamlDotNet/Serialization/SerializerBuilder.cs +++ b/YamlDotNet/Serialization/SerializerBuilder.cs @@ -286,7 +286,9 @@ public SerializerBuilder ConfigureDefaultValuesHandling(DefaultValuesHandling co /// public SerializerBuilder JsonCompatible() { - this.emitterSettings = this.emitterSettings.WithMaxSimpleKeyLength(int.MaxValue); + this.emitterSettings = this.emitterSettings + .WithMaxSimpleKeyLength(int.MaxValue) + .WithoutAnchorName(); return this .WithTypeConverter(new GuidConverter(true), w => w.InsteadOf()) @@ -554,7 +556,7 @@ public void SerializeValue(IEmitter emitter, object? value, Type? type) var preProcessingPhaseObjectGraphVisitors = preProcessingPhaseObjectGraphVisitorFactories.BuildComponentList(typeConverters); foreach (var visitor in preProcessingPhaseObjectGraphVisitors) { - traversalStrategy.Traverse(graph, visitor, null); + traversalStrategy.Traverse(graph, visitor, null!); } void nestedObjectSerializer(object? v, Type? t) => SerializeValue(emitter, v, t); @@ -568,4 +570,4 @@ public void SerializeValue(IEmitter emitter, object? value, Type? type) } } } -} \ No newline at end of file +} diff --git a/YamlDotNet/Serialization/YamlMember.cs b/YamlDotNet/Serialization/YamlMemberAttribute.cs similarity index 100% rename from YamlDotNet/Serialization/YamlMember.cs rename to YamlDotNet/Serialization/YamlMemberAttribute.cs