diff --git a/Yaml2JsonNode.Tests/Files/expected-serialized.yaml b/Yaml2JsonNode.Tests/Files/expected-serialized.yaml new file mode 100644 index 0000000000..23d358ea39 --- /dev/null +++ b/Yaml2JsonNode.Tests/Files/expected-serialized.yaml @@ -0,0 +1,15 @@ +StringProp: string +IntProp: 42 +DecimalProp: 42.5 +DoubleProp: 42.9 +BoolProp: true +ListOfInts: +- 1 +- 2 +- 3 +- 4 +MapOfStrings: + string1: found + string2: lost +NestedObject: + OtherString: yep diff --git a/Yaml2JsonNode.Tests/SerializerTests.cs b/Yaml2JsonNode.Tests/SerializerTests.cs new file mode 100644 index 0000000000..99f1218b53 --- /dev/null +++ b/Yaml2JsonNode.Tests/SerializerTests.cs @@ -0,0 +1,143 @@ +using NUnit.Framework; +// ReSharper disable NonReadonlyMemberInGetHashCode +#pragma warning disable NUnit2005 +#pragma warning disable CS8618 + +namespace Yaml2JsonNode.Tests; + +public class SerializerTests +{ + private class Bar + { + public string OtherString { get; set; } + + protected bool Equals(Bar other) + { + return OtherString == other.OtherString; + } + + public override bool Equals(object? obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((Bar)obj); + } + + public override int GetHashCode() + { + return OtherString.GetHashCode(); + } + } + + private class Foo + { + public string StringProp { get; set; } + public int IntProp { get; set; } + public decimal DecimalProp { get; set; } + public double DoubleProp { get; set; } + public bool BoolProp { get; set; } + public List ListOfInts { get; set; } + public Dictionary MapOfStrings { get; set; } + public Bar NestedObject { get; set; } + + protected bool Equals(Foo other) + { + var listEqual = ListOfInts.Zip(other.ListOfInts) + .All(x => x.First == x.Second); + var joinedMap = MapOfStrings.Join(other.MapOfStrings, + x => x.Key, + y => y.Key, + (x, y) => x.Value == y.Value) + .ToList(); + var mapEqual = joinedMap.Count == MapOfStrings.Count && joinedMap.All(x => x); + + return StringProp == other.StringProp && + IntProp == other.IntProp && + DecimalProp == other.DecimalProp && + DoubleProp.Equals(other.DoubleProp) && + BoolProp == other.BoolProp && + listEqual && + mapEqual && + NestedObject.Equals(other.NestedObject); + } + + public override bool Equals(object? obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((Foo)obj); + } + + public override int GetHashCode() + { + return HashCode.Combine(StringProp, IntProp, DecimalProp, DoubleProp, BoolProp, ListOfInts, MapOfStrings, NestedObject); + } + } + + [Test] + public void SerializeObject() + { + var foo = new Foo + { + StringProp = "string", + IntProp = 42, + DecimalProp = 42.5m, + DoubleProp = 42.9, + BoolProp = true, + ListOfInts = new List { 1, 2, 3, 4 }, + MapOfStrings = new Dictionary + { + ["string1"] = "found", + ["string2"] = "lost" + }, + NestedObject = new Bar + { + OtherString = "yep" + } + }; + + var path = Path.Combine(TestContext.CurrentContext.WorkDirectory, "Files", "expected-serialized.yaml") + .AdjustForPlatform(); + + var expected = File.ReadAllText(path); + + var actual = YamlSerializer.Serialize(foo); + + Assert.AreEqual(expected, actual); + } + + [Test] + public void DeserializeObject() + { + var path = Path.Combine(TestContext.CurrentContext.WorkDirectory, "Files", "expected-serialized.yaml") + .AdjustForPlatform(); + + var text = File.ReadAllText(path); + + var expected = new Foo + { + StringProp = "string", + IntProp = 42, + DecimalProp = 42.5m, + DoubleProp = 42.9, + BoolProp = true, + ListOfInts = new List { 1, 2, 3, 4 }, + MapOfStrings = new Dictionary + { + ["string1"] = "found", + ["string2"] = "lost" + }, + NestedObject = new Bar + { + OtherString = "yep" + } + }; + + + var actual = YamlSerializer.Deserialize(text); + + Assert.AreEqual(expected, actual); + } +} \ No newline at end of file diff --git a/Yaml2JsonNode.Tests/Yaml2JsonNode.Tests.csproj b/Yaml2JsonNode.Tests/Yaml2JsonNode.Tests.csproj index 28b603ec02..ef3a4024ac 100644 --- a/Yaml2JsonNode.Tests/Yaml2JsonNode.Tests.csproj +++ b/Yaml2JsonNode.Tests/Yaml2JsonNode.Tests.csproj @@ -24,6 +24,9 @@ + + Always + Always diff --git a/Yaml2JsonNode/Yaml2JsonNode.csproj b/Yaml2JsonNode/Yaml2JsonNode.csproj index 4fd492e9e9..c998a3449c 100644 --- a/Yaml2JsonNode/Yaml2JsonNode.csproj +++ b/Yaml2JsonNode/Yaml2JsonNode.csproj @@ -4,9 +4,9 @@ netstandard2.0 Yaml2JsonNode Greg Dennis - 1.1.1 + 1.2.0 1.0.0.0 - 1.1.1.0 + 1.2.0.0 Allows conversion of YamlDotNet's YAML models to JsonNodes. LICENSE https://github.com/gregsdennis/json-everything diff --git a/Yaml2JsonNode/YamlSerializer.cs b/Yaml2JsonNode/YamlSerializer.cs new file mode 100644 index 0000000000..55fe6eb835 --- /dev/null +++ b/Yaml2JsonNode/YamlSerializer.cs @@ -0,0 +1,104 @@ +using System; +using System.IO; +using System.Linq; +using System.Text.Json; +using YamlDotNet.RepresentationModel; +using YamlDotNet.Serialization; + +namespace Yaml2JsonNode; + +/// +/// Provides common serializer functionality. +/// +public static class YamlSerializer +{ + /// + /// Serializes an object to a YAML string + /// + /// The type of the object. + /// The object. + /// (optional) JSON serializer options. + /// (optional) YAML serializer configuration method. + /// The YAML string. + public static string Serialize(T obj, JsonSerializerOptions? options = null, Action? configure = null) + { + var json = JsonSerializer.SerializeToNode(obj, options); + var yaml = json!.ToYamlNode(); + + return Serialize(yaml, configure); + } + + /// + /// Serializes a YAML stream (a collection of documents) to a string. + /// + /// The stream. + /// (optional) YAML serializer configuration method. + /// The YAML string. + public static string Serialize(YamlStream yaml, Action? configure = null) + { + var builder = new SerializerBuilder(); + + configure?.Invoke(builder); + + var serializer = builder.Build(); + + using var writer = new StringWriter(); + + foreach (var document in yaml.Documents) + { + serializer.Serialize(writer, document.RootNode); + } + + return writer.ToString(); + } + + /// + /// Serializes a YAML document to a string. + /// + /// The document. + /// (optional) YAML serializer configuration method. + /// The YAML string. + public static string Serialize(YamlDocument yaml, Action? configure = null) + { + return Serialize(new YamlStream(yaml), configure); + } + + /// + /// Serializes a YAML node to a string + /// + /// The node. + /// (optional) YAML serializer configuration method. + /// The YAML string. + public static string Serialize(YamlNode yaml, Action? configure = null) + { + return Serialize(new YamlDocument(yaml), configure); + } + + /// + /// Deserializes the first YAML document found in text to an object. + /// + /// The type of the object. + /// The YAML text. + /// + /// The object or null. + public static T? Deserialize(string yamlText, JsonSerializerOptions? options = null) + { + var yaml = Parse(yamlText).First(); + var json = yaml.ToJsonNode(); + + return json.Deserialize(options); + } + + /// + /// Parses YAML text into a stream (collection of documents). + /// + /// The YAML text. + /// A YAML stream representing the content of the text. + public static YamlStream Parse(string yamlText) + { + using var reader = new StringReader(yamlText); + var yamlStream = new YamlStream(); + yamlStream.Load(reader); + return yamlStream; + } +} \ No newline at end of file diff --git a/tools/ApiDocsGenerator/release-notes/rn-yaml.md b/tools/ApiDocsGenerator/release-notes/rn-yaml.md index 617e438de6..72ad4c5c56 100644 --- a/tools/ApiDocsGenerator/release-notes/rn-yaml.md +++ b/tools/ApiDocsGenerator/release-notes/rn-yaml.md @@ -4,6 +4,10 @@ title: Yaml2JsonNode icon: fas fa-tag order: "8.12" --- +# [1.2.0](https://github.com/gregsdennis/json-everything/pull/475) {#release-yaml-1.2.0} + +Add `YamlSerializer` static class to provide more natural serializer methods. + # 1.1.1 (No PR) {#release-yaml-1.1.1} Fixes issue with converting strings from JSON to YAML. Previously, quotes were included.