diff --git a/.gitignore b/.gitignore index 0bc215fba..6dbeb44f6 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,7 @@ TestResults *.orig Help/* release/* -/_ReSharper.* +_ReSharper.* *.pidb *.userprefs packages diff --git a/Dockerfile b/Dockerfile index af31659b8..2437eff04 100644 --- a/Dockerfile +++ b/Dockerfile @@ -40,15 +40,15 @@ COPY . . RUN dotnet build -c Release --framework net47 YamlDotNet/YamlDotNet.csproj -o /output/net47 RUN dotnet build -c Release --framework netstandard2.0 YamlDotNet/YamlDotNet.csproj -o /output/netstandard2.0 RUN dotnet build -c Release --framework netstandard2.1 YamlDotNet/YamlDotNet.csproj -o /output/netstandard2.1 -RUN dotnet build -c Release --framework net60 YamlDotNet/YamlDotNet.csproj -o /output/net60 -RUN dotnet build -c Release --framework net70 YamlDotNet/YamlDotNet.csproj -o /output/net70 -RUN dotnet build -c Release --framework net80 YamlDotNet/YamlDotNet.csproj -o /output/net80 +RUN dotnet build -c Release --framework net6.0 YamlDotNet/YamlDotNet.csproj -o /output/net6.0 +RUN dotnet build -c Release --framework net7.0 YamlDotNet/YamlDotNet.csproj -o /output/net7.0 +RUN dotnet build -c Release --framework net8.0 YamlDotNet/YamlDotNet.csproj -o /output/net8.0 RUN dotnet pack -c Release YamlDotNet/YamlDotNet.csproj -o /output/package /p:Version=$PACKAGE_VERSION -RUN dotnet test -c Release YamlDotNet.Test/YamlDotNet.Test.csproj --framework net80 --logger:"trx;LogFileName=/output/tests.net80.trx" --logger:"console;Verbosity=detailed" -RUN dotnet test -c Release YamlDotNet.Test/YamlDotNet.Test.csproj --framework net70 --logger:"trx;LogFileName=/output/tests.net70.trx" --logger:"console;Verbosity=detailed" -RUN dotnet test -c Release YamlDotNet.Test/YamlDotNet.Test.csproj --framework net60 --logger:"trx;LogFileName=/output/tests.net60.trx" --logger:"console;Verbosity=detailed" +RUN dotnet test -c Release YamlDotNet.Test/YamlDotNet.Test.csproj --framework net8.0 --logger:"trx;LogFileName=/output/tests-net8.0.trx" --logger:"console;Verbosity=detailed" +RUN dotnet test -c Release YamlDotNet.Test/YamlDotNet.Test.csproj --framework net7.0 --logger:"trx;LogFileName=/output/tests-net7.0.trx" --logger:"console;Verbosity=detailed" +RUN dotnet test -c Release YamlDotNet.Test/YamlDotNet.Test.csproj --framework net6.0 --logger:"trx;LogFileName=/output/tests-net6.0.trx" --logger:"console;Verbosity=detailed" FROM alpine VOLUME /output diff --git a/Dockerfile.NonEnglish b/Dockerfile.NonEnglish index 69c454a7e..20d6cf1d5 100644 --- a/Dockerfile.NonEnglish +++ b/Dockerfile.NonEnglish @@ -42,9 +42,9 @@ COPY . . RUN dotnet build -c Release --framework net47 YamlDotNet/YamlDotNet.csproj -o /output/net47 RUN dotnet build -c Release --framework netstandard2.0 YamlDotNet/YamlDotNet.csproj -o /output/netstandard2.0 RUN dotnet build -c Release --framework netstandard2.1 YamlDotNet/YamlDotNet.csproj -o /output/netstandard2.1 -RUN dotnet build -c Release --framework net60 YamlDotNet/YamlDotNet.csproj -o /output/net60 -RUN dotnet build -c Release --framework net70 YamlDotNet/YamlDotNet.csproj -o /output/net70 -RUN dotnet build -c Release --framework net80 YamlDotNet/YamlDotNet.csproj -o /output/net80 +RUN dotnet build -c Release --framework net6.0 YamlDotNet/YamlDotNet.csproj -o /output/net6.0 +RUN dotnet build -c Release --framework net7.0 YamlDotNet/YamlDotNet.csproj -o /output/net7.0 +RUN dotnet build -c Release --framework net8.0 YamlDotNet/YamlDotNet.csproj -o /output/net8.0 RUN dotnet pack -c Release YamlDotNet/YamlDotNet.csproj -o /output/package /p:Version=$PACKAGE_VERSION @@ -62,10 +62,10 @@ RUN echo -n "${LC_ALL}" > /etc/locale.gen && \ apt install -y locales && \ locale-gen -RUN dotnet test -c Release YamlDotNet.Test/YamlDotNet.Test.csproj --framework net80 --logger:"trx;LogFileName=/output/tests.net80.trx" --logger:"console;Verbosity=detailed" -RUN dotnet test -c Release YamlDotNet.Test/YamlDotNet.Test.csproj --framework net70 --logger:"trx;LogFileName=/output/tests.net70.trx" --logger:"console;Verbosity=detailed" -RUN dotnet test -c Release YamlDotNet.Test/YamlDotNet.Test.csproj --framework net60 --logger:"trx;LogFileName=/output/tests.net60.trx" --logger:"console;Verbosity=detailed" -RUN dotnet test -c Release YamlDotNet.Test/YamlDotNet.Test.csproj --framework netcoreapp3.1 --logger:"trx;LogFileName=/output/tests.netcoreapp3.1.trx" --logger:"console;Verbosity=detailed" +RUN dotnet test -c Release YamlDotNet.Test/YamlDotNet.Test.csproj --framework net8.0 --logger:"trx;LogFileName=/output/tests-net8.0.trx" --logger:"console;Verbosity=detailed" +RUN dotnet test -c Release YamlDotNet.Test/YamlDotNet.Test.csproj --framework net7.0 --logger:"trx;LogFileName=/output/tests-net7.0.trx" --logger:"console;Verbosity=detailed" +RUN dotnet test -c Release YamlDotNet.Test/YamlDotNet.Test.csproj --framework net6.0 --logger:"trx;LogFileName=/output/tests-net6.0.trx" --logger:"console;Verbosity=detailed" +RUN dotnet test -c Release YamlDotNet.Test/YamlDotNet.Test.csproj --framework netcoreapp3.1 --logger:"trx;LogFileName=/output/tests-netcoreapp3.1.trx" --logger:"console;Verbosity=detailed" FROM alpine VOLUME /output diff --git a/YamlDotNet.Analyzers.StaticGenerator/YamlDotNet.Analyzers.StaticGenerator.nuspec b/YamlDotNet.Analyzers.StaticGenerator/YamlDotNet.Analyzers.StaticGenerator.nuspec index bcf94d114..e902f69d4 100644 --- a/YamlDotNet.Analyzers.StaticGenerator/YamlDotNet.Analyzers.StaticGenerator.nuspec +++ b/YamlDotNet.Analyzers.StaticGenerator/YamlDotNet.Analyzers.StaticGenerator.nuspec @@ -8,7 +8,7 @@ Roslyn Code Generator that will generate a static context for use with YamlDotNet to support ahead-of-time and library trimming. Static context generator for YamlDotNet. en-US - LICENSE.txt + MIT https://github.com/aaubry/YamlDotNet/wiki http://aaubry.net/images/yamldotnet.png images/yamldotnet.png diff --git a/YamlDotNet.Benchmark/YamlDotNet.Benchmark.csproj b/YamlDotNet.Benchmark/YamlDotNet.Benchmark.csproj index 9ade62825..79208c1f6 100644 --- a/YamlDotNet.Benchmark/YamlDotNet.Benchmark.csproj +++ b/YamlDotNet.Benchmark/YamlDotNet.Benchmark.csproj @@ -2,7 +2,7 @@ Exe - net80 + net8.0 enable enable diff --git a/YamlDotNet.Fsharp.Test/DeserializerTests.fs b/YamlDotNet.Fsharp.Test/DeserializerTests.fs new file mode 100644 index 000000000..3f48a4ff7 --- /dev/null +++ b/YamlDotNet.Fsharp.Test/DeserializerTests.fs @@ -0,0 +1,87 @@ +module DeserializerTests + +open System +open Xunit +open YamlDotNet.Serialization +open YamlDotNet.Serialization.NamingConventions +open FsUnit.Xunit +open System.ComponentModel + +[] +type Spec = { + EngineType: string + DriveType: string +} + +[] +type Car = { + Name: string + Year: int + Spec: Spec option + Nickname: string option +} + +[] +type Person = { + Name: string + MomentOfBirth: DateTime + Cars: Car array +} + +[] +let Deserialize_YamlWithScalarOptions() = + let yaml = """ +name: Jack +momentOfBirth: 1983-04-21T20:21:03.0041599Z +cars: +- name: Mercedes + year: 2018 + nickname: Jessy +- name: Honda + year: 2021 +""" + let sut = DeserializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .Build() + + let person = sut.Deserialize(yaml) + person.Name |> should equal "Jack" + person.Cars |> should haveLength 2 + person.Cars[0].Name |> should equal "Mercedes" + person.Cars[0].Nickname |> should equal (Some "Jessy") + person.Cars[1].Name |> should equal "Honda" + person.Cars[1].Nickname |> should equal None + + +[] +let Deserialize_YamlWithObjectOptions() = + let yaml = """ +name: Jack +momentOfBirth: 1983-04-21T20:21:03.0041599Z +cars: +- name: Mercedes + year: 2018 + spec: + engineType: V6 + driveType: AWD +- name: Honda + year: 2021 +""" + let sut = DeserializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .Build() + + let person = sut.Deserialize(yaml) + person.Name |> should equal "Jack" + person.Cars |> should haveLength 2 + + person.Cars[0].Name |> should equal "Mercedes" + person.Cars[0].Spec |> should not' (be null) + person.Cars[0].Spec |> Option.isSome |> should equal true + person.Cars[0].Spec.Value.EngineType |> should equal "V6" + person.Cars[0].Spec.Value.DriveType |> should equal "AWD" + + person.Cars[1].Name |> should equal "Honda" + person.Cars[1].Spec |> should be null + person.Cars[1].Spec |> should equal None + person.Cars[1].Nickname |> should equal None diff --git a/YamlDotNet.Fsharp.Test/SerializerTests.fs b/YamlDotNet.Fsharp.Test/SerializerTests.fs new file mode 100644 index 000000000..d612647cc --- /dev/null +++ b/YamlDotNet.Fsharp.Test/SerializerTests.fs @@ -0,0 +1,152 @@ +module SerializerTests + +open System +open Xunit +open YamlDotNet.Serialization +open YamlDotNet.Serialization.NamingConventions +open FsUnit.Xunit +open YamlDotNet.Core + +[] +type Spec = { + EngineType: string + DriveType: string +} + +[] +type Car = { + Name: string + Year: int + Spec: Spec option + Nickname: string option +} + +[] +type Person = { + Name: string + MomentOfBirth: DateTime + KidsSeat: int option + Cars: Car array +} + +[] +let Serialize_YamlWithScalarOptions() = + let jackTheDriver = { + Name = "Jack" + MomentOfBirth = DateTime(1983, 4, 21, 20, 21, 03, 4) + KidsSeat = Some 1 + Cars = [| + { Name = "Mercedes" + Year = 2018 + Nickname = Some "Jessy" + Spec = None }; + { Name = "Honda" + Year = 2021 + Nickname = None + Spec = None } + |] + } + + let yaml = """name: Jack +momentOfBirth: 1983-04-21T20:21:03.0040000 +kidsSeat: 1 +cars: +- name: Mercedes + year: 2018 + spec: + nickname: Jessy +- name: Honda + year: 2021 + spec: + nickname: +""" + let sut = SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .Build() + + let person = sut.Serialize(jackTheDriver) + person |> should equal yaml + + +[] +let Serialize_YamlWithScalarOptions_OmitNull() = + let jackTheDriver = { + Name = "Jack" + MomentOfBirth = DateTime(1983, 4, 21, 20, 21, 03, 4) + KidsSeat = Some 1 + Cars = [| + { Name = "Mercedes" + Year = 2018 + Nickname = Some "Jessy" + Spec = None }; + { Name = "Honda" + Year = 2021 + Nickname = None + Spec = None } + |] + } + + let yaml = """name: Jack +momentOfBirth: 1983-04-21T20:21:03.0040000 +kidsSeat: 1 +cars: +- name: Mercedes + year: 2018 + nickname: Jessy +- name: Honda + year: 2021 +""" + let sut = SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull) + .Build() + + let person = sut.Serialize(jackTheDriver) + person |> should equal yaml + + +[] +let Serialize_YamlWithObjectOptions_OmitNull() = + let jackTheDriver = { + Name = "Jack" + MomentOfBirth = DateTime(1983, 4, 21, 20, 21, 03, 4) + KidsSeat = Some 1 + Cars = [| + { Name = "Mercedes" + Year = 2018 + Nickname = None + Spec = Some { + EngineType = "V6" + DriveType = "AWD" + } }; + { Name = "Honda" + Year = 2021 + Nickname = None + Spec = None } + |] + } + + let yaml = """name: Jack +momentOfBirth: 1983-04-21T20:21:03.0040000 +kidsSeat: 1 +cars: +- name: Mercedes + year: 2018 + spec: + engineType: V6 + driveType: AWD +- name: Honda + year: 2021 +""" + let sut = SerializerBuilder() + .WithNamingConvention(CamelCaseNamingConvention.Instance) + .ConfigureDefaultValuesHandling(DefaultValuesHandling.OmitNull) + .Build() + + let person = sut.Serialize(jackTheDriver) + person |> should equal yaml + +type TestOmit = { + name: string + plop: int option +} diff --git a/YamlDotNet.Fsharp.Test/YamlDotNet.Fsharp.Test.fsproj b/YamlDotNet.Fsharp.Test/YamlDotNet.Fsharp.Test.fsproj new file mode 100644 index 000000000..300cec1f9 --- /dev/null +++ b/YamlDotNet.Fsharp.Test/YamlDotNet.Fsharp.Test.fsproj @@ -0,0 +1,25 @@ + + + net8.0;net7.0;net6.0;net47 + false + ..\YamlDotNet.snk + true + 8.0 + true + + + + + + + + + + + + + + + + + diff --git a/YamlDotNet.Samples/YamlDotNet.Samples.csproj b/YamlDotNet.Samples/YamlDotNet.Samples.csproj index 5ca9b2c46..e8d4b0d0e 100644 --- a/YamlDotNet.Samples/YamlDotNet.Samples.csproj +++ b/YamlDotNet.Samples/YamlDotNet.Samples.csproj @@ -1,7 +1,7 @@  - net80 + net8.0 false diff --git a/YamlDotNet.Test/RepresentationModel/YamlStreamTests.cs b/YamlDotNet.Test/RepresentationModel/YamlStreamTests.cs index d1475c5fe..913f5aa89 100644 --- a/YamlDotNet.Test/RepresentationModel/YamlStreamTests.cs +++ b/YamlDotNet.Test/RepresentationModel/YamlStreamTests.cs @@ -47,22 +47,26 @@ public void LoadSimpleDocument() Assert.Equal(YamlNodeType.Scalar, stream.Documents[0].RootNode.NodeType); } - [Fact] - public void AccessingAllNodesOnInfinitelyRecursiveDocumentThrows() + [Theory] + [InlineData("&a [*a]")] + [InlineData("?\n key: &id1\n recursion: *id1\n: foo")] + public void AccessingAllNodesOnInfinitelyRecursiveDocumentThrows(string yaml) { var stream = new YamlStream(); - stream.Load(Yaml.ParserForText("&a [*a]")); + stream.Load(Yaml.ParserForText(yaml)); var accessAllNodes = new Action(() => stream.Documents.Single().AllNodes.ToList()); accessAllNodes.ShouldThrow("because the document is infinitely recursive."); } - [Fact] - public void InfinitelyRecursiveNodeToStringSucceeds() + [Theory] + [InlineData("&a [*a]")] + [InlineData("?\n key: &id1\n recursion: *id1\n: foo")] + public void InfinitelyRecursiveNodeToStringSucceeds(string yaml) { var stream = new YamlStream(); - stream.Load(Yaml.ParserForText("&a [*a]")); + stream.Load(Yaml.ParserForText(yaml)); var toString = stream.Documents.Single().RootNode.ToString(); diff --git a/YamlDotNet.Test/Serialization/DeserializerTest.cs b/YamlDotNet.Test/Serialization/DeserializerTest.cs index 95ce8a39a..526596fc3 100644 --- a/YamlDotNet.Test/Serialization/DeserializerTest.cs +++ b/YamlDotNet.Test/Serialization/DeserializerTest.cs @@ -265,7 +265,7 @@ public static IEnumerable DeserializeScalarEdgeCases_TestCases yield return new object[] { decimal.MaxValue, typeof(decimal) }; yield return new object[] { char.MaxValue, typeof(char) }; -#if NETCOREAPP3_1_OR_GREATER +#if NET yield return new object[] { float.MinValue, typeof(float) }; yield return new object[] { float.MaxValue, typeof(float) }; yield return new object[] { double.MinValue, typeof(double) }; diff --git a/YamlDotNet.Test/Serialization/SerializationTests.cs b/YamlDotNet.Test/Serialization/SerializationTests.cs index 1e67eacef..0d619904a 100644 --- a/YamlDotNet.Test/Serialization/SerializationTests.cs +++ b/YamlDotNet.Test/Serialization/SerializationTests.cs @@ -1554,7 +1554,7 @@ public static IEnumerable SpecialFloats new FloatTestCase("float.Epsilon", float.Epsilon, float.Epsilon.ToString("G", CultureInfo.InvariantCulture)), new FloatTestCase("float.26.67", 26.67F, "26.67"), -#if NETCOREAPP3_1_OR_GREATER +#if NET new FloatTestCase("double.MinValue", double.MinValue, double.MinValue.ToString("G", CultureInfo.InvariantCulture)), new FloatTestCase("double.MaxValue", double.MaxValue, double.MaxValue.ToString("G", CultureInfo.InvariantCulture)), new FloatTestCase("float.MinValue", float.MinValue, float.MinValue.ToString("G", CultureInfo.InvariantCulture)), @@ -2499,6 +2499,16 @@ public void NamingConventionAppliedToEnumWhenDeserializing() Assert.Equal(expected, actual); } + [Fact] + [Trait("motive", "issue #656")] + public void NestedDictionaryTypes_ShouldRoundtrip() + { + var serializer = new SerializerBuilder().EnsureRoundtrip().Build(); + var yaml = serializer.Serialize(new HasNestedDictionary { Lookups = { [1] = new HasNestedDictionary.Payload { I = 1 } } }, typeof(HasNestedDictionary)); + var dct = new DeserializerBuilder().Build().Deserialize(yaml); + Assert.Contains(new KeyValuePair(1, new HasNestedDictionary.Payload { I = 1 }), dct.Lookups); + } + public class TestState { public int OnSerializedCallCount { get; set; } @@ -2590,5 +2600,15 @@ public void WriteYaml(IEmitter emitter, object value, Type type, ObjectSerialize emitter.Emit(new Scalar(((NonSerializable)value).Text)); } } + + public sealed class HasNestedDictionary + { + public Dictionary Lookups { get; set; } = new Dictionary(); + + public struct Payload + { + public int I { get; set; } + } + } } } diff --git a/YamlDotNet.Test/YamlDotNet.Test.csproj b/YamlDotNet.Test/YamlDotNet.Test.csproj index 49f46e4dd..77f2dc142 100644 --- a/YamlDotNet.Test/YamlDotNet.Test.csproj +++ b/YamlDotNet.Test/YamlDotNet.Test.csproj @@ -1,6 +1,6 @@  - net80;net70;net60;net47 + net8.0;net7.0;net6.0;net47 false ..\YamlDotNet.snk true diff --git a/YamlDotNet.sln b/YamlDotNet.sln index e7d7d763e..b88747fc1 100644 --- a/YamlDotNet.sln +++ b/YamlDotNet.sln @@ -31,6 +31,8 @@ Project("{6EC3EE1D-3C4E-46DD-8F32-0CC8E7565705}") = "YamlDotNet.Samples.Fsharp", EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "YamlDotNet.Core7AoTCompileTest.Model", "YamlDotNet.Core7AoTCompileTest.Model\YamlDotNet.Core7AoTCompileTest.Model.csproj", "{BFE15564-7C2C-47DA-8302-9BCB39B6864B}" EndProject +Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "YamlDotNet.Fsharp.Test", "YamlDotNet.Fsharp.Test\YamlDotNet.Fsharp.Test.fsproj", "{294EFEB3-4DC2-4105-ADE7-E429F5522419}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -69,6 +71,10 @@ Global {BFE15564-7C2C-47DA-8302-9BCB39B6864B}.Debug|Any CPU.Build.0 = Debug|Any CPU {BFE15564-7C2C-47DA-8302-9BCB39B6864B}.Release|Any CPU.ActiveCfg = Release|Any CPU {BFE15564-7C2C-47DA-8302-9BCB39B6864B}.Release|Any CPU.Build.0 = Release|Any CPU + {294EFEB3-4DC2-4105-ADE7-E429F5522419}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {294EFEB3-4DC2-4105-ADE7-E429F5522419}.Debug|Any CPU.Build.0 = Debug|Any CPU + {294EFEB3-4DC2-4105-ADE7-E429F5522419}.Release|Any CPU.ActiveCfg = Release|Any CPU + {294EFEB3-4DC2-4105-ADE7-E429F5522419}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/YamlDotNet/Helpers/FsharpHelper.cs b/YamlDotNet/Helpers/FsharpHelper.cs new file mode 100644 index 000000000..304164b5f --- /dev/null +++ b/YamlDotNet/Helpers/FsharpHelper.cs @@ -0,0 +1,38 @@ +using System; +using YamlDotNet.Serialization; + +namespace YamlDotNet.Helpers +{ + public static class FsharpHelper + { + private static bool IsFsharp(Type t) + { + return t.Namespace == "Microsoft.FSharp.Core"; + } + + public static bool IsOptionType(Type t) + { + return IsFsharp(t) && t.Name == "FSharpOption`1"; + } + + public static Type? GetOptionUnderlyingType(Type t) + { + return t.IsGenericType && IsOptionType(t) ? t.GenericTypeArguments[0] : null; + } + + public static object? GetValue(IObjectDescriptor objectDescriptor) + { + if (!IsOptionType(objectDescriptor.Type)) + { + throw new InvalidOperationException("Should not be called on non-Option<> type"); + } + + if (objectDescriptor.Value is null) + { + return null; + } + + return objectDescriptor.Type.GetProperty("Value").GetValue(objectDescriptor.Value); + } + } +} diff --git a/YamlDotNet/Helpers/OrderedDictionary.cs b/YamlDotNet/Helpers/OrderedDictionary.cs index 7d5ae95f1..94e21758b 100644 --- a/YamlDotNet/Helpers/OrderedDictionary.cs +++ b/YamlDotNet/Helpers/OrderedDictionary.cs @@ -136,14 +136,14 @@ public void RemoveAt(int index) list.RemoveAt(index); } -#if !(NETCOREAPP3_1) +#if !NET #pragma warning disable 8767 // Nullability of reference types in type of parameter ... doesn't match implicitly implemented member #endif public bool TryGetValue(TKey key, [MaybeNullWhen(false)] out TValue value) => dictionary.TryGetValue(key, out value); -#if !(NETCOREAPP3_1) +#if !NET #pragma warning restore 8767 #endif diff --git a/YamlDotNet/RepresentationModel/YamlMappingNode.cs b/YamlDotNet/RepresentationModel/YamlMappingNode.cs index 3aa34d9ad..b356f88c9 100644 --- a/YamlDotNet/RepresentationModel/YamlMappingNode.cs +++ b/YamlDotNet/RepresentationModel/YamlMappingNode.cs @@ -297,7 +297,9 @@ public override int GetHashCode() foreach (var entry in children) { hashCode = CombineHashCodes(hashCode, entry.Key); - hashCode = CombineHashCodes(hashCode, entry.Value); + hashCode = entry.Value.Anchor.IsEmpty + ? CombineHashCodes(hashCode, entry.Value) + : CombineHashCodes(hashCode, entry.Value.Anchor); } return hashCode; } diff --git a/YamlDotNet/Serialization/NodeDeserializers/ObjectNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/ObjectNodeDeserializer.cs index 728ba4b39..b2a179c5d 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/ObjectNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/ObjectNodeDeserializer.cs @@ -26,6 +26,7 @@ using System.Runtime.Serialization; using YamlDotNet.Core; using YamlDotNet.Core.Events; +using YamlDotNet.Helpers; using YamlDotNet.Serialization.Utilities; namespace YamlDotNet.Serialization.NodeDeserializers @@ -73,8 +74,11 @@ public bool Deserialize(IParser parser, Type expectedType, Func(IPropertyDescriptor? propertyDescripto break; } - var underlyingType = Nullable.GetUnderlyingType(value.Type); - if (underlyingType != null) + var nullableUnderlyingType = Nullable.GetUnderlyingType(value.Type); + var optionUnderlyingType = nullableUnderlyingType ?? FsharpHelper.GetOptionUnderlyingType(value.Type); + var optionValue = optionUnderlyingType != null ? FsharpHelper.GetValue(value) : null; + + if (nullableUnderlyingType != null) { // This is a nullable type, recursively handle it with its underlying type. // Note that if it contains null, the condition above already took care of it - Traverse(propertyDescriptor, "Value", new ObjectDescriptor(value.Value, underlyingType, value.Type, value.ScalarStyle), visitor, context, path, serializer); + Traverse( + "Value", + new ObjectDescriptor(value.Value, nullableUnderlyingType, value.Type, value.ScalarStyle), + visitor, + context, + path, + serializer + ); + } + else if (optionUnderlyingType != null && optionValue != null) + { + Traverse( + "Value", + new ObjectDescriptor(FsharpHelper.GetValue(value), optionUnderlyingType, value.Type, value.ScalarStyle), + visitor, + context, + path, + serializer + ); } else { diff --git a/YamlDotNet/Serialization/Utilities/TypeConverter.cs b/YamlDotNet/Serialization/Utilities/TypeConverter.cs index d26f082a0..e6acf74b3 100644 --- a/YamlDotNet/Serialization/Utilities/TypeConverter.cs +++ b/YamlDotNet/Serialization/Utilities/TypeConverter.cs @@ -27,6 +27,7 @@ using System.Globalization; using System.Reflection; using System.ComponentModel; +using YamlDotNet.Helpers; namespace YamlDotNet.Serialization.Utilities { @@ -100,11 +101,11 @@ public static T ChangeType(object? value, INamingConvention enumNamingConvent return value; } - // Nullable types get a special treatment + // Nullable & fsharp option types get a special treatment if (destinationType.IsGenericType()) { var genericTypeDefinition = destinationType.GetGenericTypeDefinition(); - if (genericTypeDefinition == typeof(Nullable<>)) + if (genericTypeDefinition == typeof(Nullable<>) || FsharpHelper.IsOptionType(genericTypeDefinition)) { var innerType = destinationType.GetGenericArguments()[0]; var convertedValue = ChangeType(value, innerType, culture, enumNamingConvention, typeInspector); diff --git a/YamlDotNet/YamlDotNet.csproj b/YamlDotNet/YamlDotNet.csproj index 482c5fb2b..516721292 100644 --- a/YamlDotNet/YamlDotNet.csproj +++ b/YamlDotNet/YamlDotNet.csproj @@ -1,12 +1,12 @@  - net80;net70;net60;netstandard2.0;netstandard2.1;net47 + net8.0;net7.0;net6.0;netstandard2.0;netstandard2.1;net47 https://github.com/aaubry/YamlDotNet https://github.com/aaubry/YamlDotNet YamlDotNet is a .NET library for YAML. YamlDotNet provides low level parsing and emitting of YAML as well as a high level object model similar to XmlDocument. A serialization library is also included that allows to read and write objects from and to YAML streams. - LICENSE.txt + MIT Copyright (c) Antoine Aubry and contributors Debug;Release ..\YamlDotNet.snk @@ -21,9 +21,13 @@ false $(TargetFramework) + + true + true + snupkg - + 1591;1574;8600;8602;8604 @@ -36,19 +40,20 @@ true - + true - + true - + true + @@ -58,37 +63,28 @@ $(DefineConstants);RELEASE;TRACE;SIGNED - false - portable - true true - + + true + + - - 4.3.0 - - - 4.3.0 - - - 4.3.0 - + + + - - - diff --git a/YamlDotNet/YamlDotNet.nuspec b/YamlDotNet/YamlDotNet.nuspec index e5f4046d1..4c922a3de 100644 --- a/YamlDotNet/YamlDotNet.nuspec +++ b/YamlDotNet/YamlDotNet.nuspec @@ -7,12 +7,13 @@ A .NET library for YAML. YamlDotNet provides low level parsing and emitting of YAML as well as a high level object model similar to XmlDocument. This package contains the YAML parser and serializer. en-US - LICENSE.txt + MIT https://github.com/aaubry/YamlDotNet/wiki http://aaubry.net/images/yamldotnet.png images/yamldotnet.png yaml parser development library serialization + README.md @@ -24,23 +25,30 @@ + - - + + + - - + + + - - + + + + + + diff --git a/appveyor.yml b/appveyor.yml index 578243836..2ea55a634 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -35,13 +35,14 @@ artifacts: - path: YamlDotNet\bin\Release\net47 name: Release-Net47 - - path: YamlDotNet\bin\Release\net60 + - path: YamlDotNet\bin\Release\net6.0 name: Release-Net60 - - path: YamlDotNet\bin\Release\net70 + - path: YamlDotNet\bin\Release\net7.0 name: Release-Net70 - - path: YamlDotNet\bin\Release\net80 + - path: YamlDotNet\bin\Release\net8.0 name: Release-Net80 - path: YamlDotNet\bin\*.nupkg + - path: YamlDotNet\bin\*.snupkg diff --git a/tools/build/BuildDefinition.cs b/tools/build/BuildDefinition.cs index 415a4470b..a409e0881 100644 --- a/tools/build/BuildDefinition.cs +++ b/tools/build/BuildDefinition.cs @@ -125,7 +125,7 @@ public static Task SetMetadata(GitVersion version) public static Task Build(Options options, MetadataSet _) { var verbosity = options.Verbose ? "detailed" : "minimal"; - Run("dotnet", $"build YamlDotNet.sln --configuration Release --verbosity {verbosity}", BasePath); + Run("dotnet", $"build YamlDotNet.sln --configuration Release --verbosity {verbosity} -p:ContinuousIntegrationBuild=true", BasePath); return Task.FromResult(new SuccessfulBuild()); } @@ -143,14 +143,14 @@ public static Task> Pack(Options options, GitVersion version, var result = new List(); var verbosity = options.Verbose ? "detailed" : "minimal"; var buildDir = Path.Combine(BasePath, "YamlDotNet"); - Run("nuget", $"pack YamlDotNet.nuspec -Version {version.NuGetVersion} -OutputDirectory bin", buildDir); + Run("nuget", $"pack YamlDotNet.nuspec -Version {version.NuGetVersion} -OutputDirectory bin -Symbols -SymbolPackageFormat snupkg", buildDir); var packagePath = Path.Combine(buildDir, "bin", $"YamlDotNet.{version.NuGetVersion}.nupkg"); result.Add(new NuGetPackage(packagePath, "YamlDotNet")); if (PushSerializer) { buildDir = Path.Combine(BasePath, "YamlDotNet.Analyzers.StaticGenerator"); - Run("nuget", $"pack YamlDotNet.Analyzers.StaticGenerator.nuspec -Version {version.NuGetVersion} -OutputDirectory bin", buildDir); + Run("nuget", $"pack YamlDotNet.Analyzers.StaticGenerator.nuspec -Version {version.NuGetVersion} -OutputDirectory bin -Symbols -SymbolPackageFormat snupkg", buildDir); packagePath = Path.Combine(buildDir, "bin", $"YamlDotNet.Analyzers.StaticGenerator.{version.NuGetVersion}.nupkg"); result.Add(new NuGetPackage(packagePath, "YamlDotNet.Analyzers.StaticGenerator")); } diff --git a/tools/build/build.csproj b/tools/build/build.csproj index 25deec28c..7d1028c1e 100644 --- a/tools/build/build.csproj +++ b/tools/build/build.csproj @@ -2,7 +2,7 @@ Exe - net80 + net8.0 AnyCPU Enable