diff --git a/.dockerignore b/.dockerignore index 191381ee..27c79530 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1 +1,5 @@ -.git \ No newline at end of file +.git +Dockerfile.NonEnglish +Dockerfile +bin +obj \ No newline at end of file diff --git a/Dockerfile b/Dockerfile index 05eb691b..1c7f009f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ RUN apt install -y wget RUN wget https://packages.microsoft.com/config/debian/11/packages-microsoft-prod.deb -O packages-microsoft-prod.deb RUN dpkg -i packages-microsoft-prod.deb RUN apt update -RUN apt install -y dotnet-sdk-3.1 +RUN apt install -y dotnet-sdk-3.1 dotnet-sdk-6.0 FROM builder AS build WORKDIR /src @@ -46,10 +46,9 @@ RUN dotnet build -c Release --framework net70 YamlDotNet/YamlDotNet.csproj -o /o RUN dotnet pack -c Release YamlDotNet/YamlDotNet.csproj -o /output/package /p:Version=$PACKAGE_VERSION -RUN dotnet test -c Release YamlDotNet.sln --framework net70 --logger:"trx;LogFileName=/output/tests.net60.trx" --logger:"console;Verbosity=detailed" -RUN dotnet test -c Release YamlDotNet.sln --framework net60 --logger:"trx;LogFileName=/output/tests.net60.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 net47 --logger:"trx;LogFileName=/output/tests.net47.trx" --logger:"console;Verbosity=detailed" FROM alpine VOLUME /output diff --git a/Dockerfile.NonEnglish b/Dockerfile.NonEnglish new file mode 100644 index 00000000..fe85b5e9 --- /dev/null +++ b/Dockerfile.NonEnglish @@ -0,0 +1,73 @@ +FROM mcr.microsoft.com/dotnet/sdk:7.0 AS builder +RUN apt update && \ + apt install -y \ + apt-transport-https \ + gnupg \ + ca-certificates \ + curl + +RUN apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 3FA7E0328081BFF6A14DA29AA6A19B38D3D831EF +# yes, we're using the bullseye (debian 11) image, however mono only has buster, but it still works for bullseye +RUN echo "deb https://download.mono-project.com/repo/debian stable-buster main" > /etc/apt/sources.list.d/mono-official-stable.list +RUN apt update +RUN apt install -y mono-complete + +# install dot net 3.1 for running netstandard2.1 tests +RUN apt install -y wget +RUN wget https://packages.microsoft.com/config/debian/11/packages-microsoft-prod.deb -O packages-microsoft-prod.deb +RUN dpkg -i packages-microsoft-prod.deb +RUN apt update +RUN apt install -y dotnet-sdk-3.1 dotnet-sdk-6.0 + + +FROM builder AS build +WORKDIR /src +ARG PACKAGE_VERSION=1.0.0 + +COPY YamlDotNet.sln YamlDotNet.sln +COPY YamlDotNet/YamlDotNet.csproj YamlDotNet/YamlDotNet.csproj +COPY YamlDotNet.AotTest/YamlDotNet.AotTest.csproj YamlDotNet.AotTest/YamlDotNet.AotTest.csproj +COPY YamlDotNet.Benchmark/YamlDotNet.Benchmark.csproj YamlDotNet.Benchmark/YamlDotNet.Benchmark.csproj +COPY YamlDotNet.Samples/YamlDotNet.Samples.csproj YamlDotNet.Samples/YamlDotNet.Samples.csproj +COPY YamlDotNet.Test/YamlDotNet.Test.csproj YamlDotNet.Test/YamlDotNet.Test.csproj +COPY YamlDotNet.Analyzers.StaticGenerator/YamlDotNet.Analyzers.StaticGenerator.csproj YamlDotNet.Analyzers.StaticGenerator/YamlDotNet.Analyzers.StaticGenerator.csproj +COPY YamlDotNet.Core7AoTCompileTest/YamlDotNet.Core7AoTCompileTest.csproj YamlDotNet.Core7AoTCompileTest/YamlDotNet.Core7AoTCompileTest.csproj + +RUN dotnet restore YamlDotNet.sln + +COPY . . + +RUN dotnet build -c Release --framework net35 YamlDotNet/YamlDotNet.csproj -o /output/net35 +RUN dotnet build -c Release --framework net40 YamlDotNet/YamlDotNet.csproj -o /output/net40 +RUN dotnet build -c Release --framework net45 YamlDotNet/YamlDotNet.csproj -o /output/net45 +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 pack -c Release YamlDotNet/YamlDotNet.csproj -o /output/package /p:Version=$PACKAGE_VERSION + +ARG LOCALE_LANGUAGE="en" +ARG LOCALE_COUNTRY="DK" +ARG LOCALE_LOCALE="${LOCALE_LANGUAGE}_${LOCALE_COUNTRY}" +ARG LOCALE_ENCODING="UTF-8" + +ENV LANG="${LOCALE_LANGUAGE}_${LOCALE_COUNTRY}.${LOCALE_ENCODING}" +ENV LANGUAGE="${LOCALE_LANGUAGE}_${LOCALE_COUNTRY}:${LOCALE_LANGUAGE}" +ENV LC_ALL="${LOCALE_LANGUAGE}_${LOCALE_COUNTRY}.${LOCALE_ENCODING}" + +RUN echo -n "${LC_ALL}" > /etc/locale.gen && \ + printenv && \ + apt install -y locales && \ + locale-gen + +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" + +FROM alpine +VOLUME /output +WORKDIR /libraries +COPY --from=build /output /libraries +CMD [ "cp", "-r", "/libraries", "/output" ] \ No newline at end of file diff --git a/YamlDotNet.Core7AoTCompileTest/Program.cs b/YamlDotNet.Core7AoTCompileTest/Program.cs index 65f1146e..6b07db50 100644 --- a/YamlDotNet.Core7AoTCompileTest/Program.cs +++ b/YamlDotNet.Core7AoTCompileTest/Program.cs @@ -18,6 +18,9 @@ // 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. +#pragma warning disable CS8604 // Possible null reference argument. +#pragma warning disable CS8618 // Possible null reference argument. +#pragma warning disable CS8602 // Possible null reference argument. using System; using System.Collections; @@ -208,3 +211,6 @@ public enum MyTestEnum Z = 1, } +#pragma warning restore CS8604 // Possible null reference argument. +#pragma warning restore CS8618 // Possible null reference argument. +#pragma warning restore CS8602 // Possible null reference argument. diff --git a/YamlDotNet.Samples/UseTypeConverters.cs b/YamlDotNet.Samples/UseTypeConverters.cs index 8459428b..f60f0d73 100644 --- a/YamlDotNet.Samples/UseTypeConverters.cs +++ b/YamlDotNet.Samples/UseTypeConverters.cs @@ -48,7 +48,7 @@ public void Main() var serializer = new SerializerBuilder() .WithTypeConverter(new DateTimeOffsetConverter()) .Build(); - var o = new { Hello = new DateTimeOffset(DateTime.Now, new TimeSpan(-6, 0, 0)) }; + var o = new { Hello = DateTime.UtcNow }; var yaml = serializer.Serialize(o); output.WriteLine(yaml); } diff --git a/YamlDotNet.Test/Serialization/DeserializerTest.cs b/YamlDotNet.Test/Serialization/DeserializerTest.cs index f715fd1e..b62af24a 100644 --- a/YamlDotNet.Test/Serialization/DeserializerTest.cs +++ b/YamlDotNet.Test/Serialization/DeserializerTest.cs @@ -278,7 +278,7 @@ public static IEnumerable DeserializeScalarEdgeCases_TestCases public void DeserializeScalarEdgeCases(IConvertible value, Type type) { var deserializer = new DeserializerBuilder().Build(); - var result = deserializer.Deserialize(value.ToString(), type); + var result = deserializer.Deserialize(value.ToString(YamlFormatter.Default.NumberFormat), type); result.Should().Be(value); } diff --git a/YamlDotNet/Serialization/BuilderSkeleton.cs b/YamlDotNet/Serialization/BuilderSkeleton.cs index 09793579..278eb44b 100755 --- a/YamlDotNet/Serialization/BuilderSkeleton.cs +++ b/YamlDotNet/Serialization/BuilderSkeleton.cs @@ -42,6 +42,7 @@ public abstract class BuilderSkeleton internal bool ignoreFields; internal bool includeNonPublicProperties = false; internal Settings settings; + internal YamlFormatter yamlFormatter = YamlFormatter.Default; internal BuilderSkeleton(ITypeResolver typeResolver) { @@ -295,6 +296,18 @@ public TBuilder WithoutTypeInspector(Type inspectorType) return Self; } + /// + /// Override the default yaml formatter with the one passed in + /// + /// to use when serializing and deserializing objects. + /// + /// + public TBuilder WithYamlFormatter(YamlFormatter formatter) + { + yamlFormatter = formatter ?? throw new ArgumentNullException(nameof(formatter)); + return Self; + } + protected IEnumerable BuildTypeConverters() { return typeConverterFactories.BuildComponentList(); diff --git a/YamlDotNet/Serialization/DeserializerBuilder.cs b/YamlDotNet/Serialization/DeserializerBuilder.cs index 5fafb145..d1faf54d 100755 --- a/YamlDotNet/Serialization/DeserializerBuilder.cs +++ b/YamlDotNet/Serialization/DeserializerBuilder.cs @@ -92,7 +92,7 @@ public DeserializerBuilder() { typeof(YamlSerializableNodeDeserializer), _ => new YamlSerializableNodeDeserializer(objectFactory.Value) }, { typeof(TypeConverterNodeDeserializer), _ => new TypeConverterNodeDeserializer(BuildTypeConverters()) }, { typeof(NullNodeDeserializer), _ => new NullNodeDeserializer() }, - { typeof(ScalarNodeDeserializer), _ => new ScalarNodeDeserializer(attemptUnknownTypeDeserialization, typeConverter) }, + { typeof(ScalarNodeDeserializer), _ => new ScalarNodeDeserializer(attemptUnknownTypeDeserialization, typeConverter, yamlFormatter) }, { typeof(ArrayNodeDeserializer), _ => new ArrayNodeDeserializer() }, { typeof(DictionaryNodeDeserializer), _ => new DictionaryNodeDeserializer(objectFactory.Value, duplicateKeyChecking) }, { typeof(CollectionNodeDeserializer), _ => new CollectionNodeDeserializer(objectFactory.Value) }, diff --git a/YamlDotNet/Serialization/EventEmitters/JsonEventEmitter.cs b/YamlDotNet/Serialization/EventEmitters/JsonEventEmitter.cs index 3d96a728..e7908709 100644 --- a/YamlDotNet/Serialization/EventEmitters/JsonEventEmitter.cs +++ b/YamlDotNet/Serialization/EventEmitters/JsonEventEmitter.cs @@ -27,8 +27,16 @@ namespace YamlDotNet.Serialization.EventEmitters { public sealed class JsonEventEmitter : ChainedEventEmitter { - public JsonEventEmitter(IEventEmitter nextEmitter) + private readonly YamlFormatter formatter; + + public JsonEventEmitter(IEventEmitter nextEmitter, YamlFormatter formatter) : base(nextEmitter) + { + this.formatter = formatter; + } + + public JsonEventEmitter(IEventEmitter nextEmitter) + : this(nextEmitter, YamlFormatter.Default) { } @@ -53,7 +61,7 @@ public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) switch (typeCode) { case TypeCode.Boolean: - eventInfo.RenderedValue = YamlFormatter.FormatBoolean(value); + eventInfo.RenderedValue = formatter.FormatBoolean(value); break; case TypeCode.Byte: @@ -72,13 +80,13 @@ public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) break; } - eventInfo.RenderedValue = YamlFormatter.FormatNumber(value); + eventInfo.RenderedValue = formatter.FormatNumber(value); break; case TypeCode.Single: case TypeCode.Double: case TypeCode.Decimal: - eventInfo.RenderedValue = YamlFormatter.FormatNumber(value); + eventInfo.RenderedValue = formatter.FormatNumber(value); break; case TypeCode.String: @@ -88,7 +96,7 @@ public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) break; case TypeCode.DateTime: - eventInfo.RenderedValue = YamlFormatter.FormatDateTime(value); + eventInfo.RenderedValue = formatter.FormatDateTime(value); break; case TypeCode.Empty: @@ -98,7 +106,7 @@ public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) default: if (eventInfo.Source.Type == typeof(TimeSpan)) { - eventInfo.RenderedValue = YamlFormatter.FormatTimeSpan(value); + eventInfo.RenderedValue = formatter.FormatTimeSpan(value); break; } diff --git a/YamlDotNet/Serialization/EventEmitters/TypeAssigningEventEmitter.cs b/YamlDotNet/Serialization/EventEmitters/TypeAssigningEventEmitter.cs index 2633ad5c..2f2ec180 100644 --- a/YamlDotNet/Serialization/EventEmitters/TypeAssigningEventEmitter.cs +++ b/YamlDotNet/Serialization/EventEmitters/TypeAssigningEventEmitter.cs @@ -21,6 +21,8 @@ using System; using System.Collections.Generic; +using System.Runtime.InteropServices.ComTypes; +using System.Runtime.Serialization; using System.Text.RegularExpressions; using YamlDotNet.Core; using YamlDotNet.Serialization.Schemas; @@ -66,22 +68,14 @@ public sealed class TypeAssigningEventEmitter : ChainedEventEmitter + @")$"; private readonly ScalarStyle defaultScalarStyle = ScalarStyle.Any; + private readonly YamlFormatter formatter; - public TypeAssigningEventEmitter(IEventEmitter nextEmitter, bool requireTagWhenStaticAndActualTypesAreDifferent, IDictionary tagMappings, bool quoteNecessaryStrings, bool quoteYaml1_1Strings, ScalarStyle defaultScalarStyle) - : this(nextEmitter, requireTagWhenStaticAndActualTypesAreDifferent, tagMappings, quoteNecessaryStrings, quoteYaml1_1Strings) - { - this.defaultScalarStyle = defaultScalarStyle; - } - - public TypeAssigningEventEmitter(IEventEmitter nextEmitter, bool requireTagWhenStaticAndActualTypesAreDifferent, IDictionary tagMappings, bool quoteNecessaryStrings, ScalarStyle defaultScalarStyle) - : this(nextEmitter, requireTagWhenStaticAndActualTypesAreDifferent, tagMappings, quoteNecessaryStrings) + public TypeAssigningEventEmitter(IEventEmitter nextEmitter, bool requireTagWhenStaticAndActualTypesAreDifferent, IDictionary tagMappings, bool quoteNecessaryStrings, bool quoteYaml1_1Strings, ScalarStyle defaultScalarStyle, YamlFormatter formatter) + : base(nextEmitter) { this.defaultScalarStyle = defaultScalarStyle; - } - - public TypeAssigningEventEmitter(IEventEmitter nextEmitter, bool requireTagWhenStaticAndActualTypesAreDifferent, IDictionary tagMappings, bool quoteNecessaryStrings, bool quoteYaml1_1Strings) - : this(nextEmitter, requireTagWhenStaticAndActualTypesAreDifferent, tagMappings) - { + this.formatter = formatter; + this.tagMappings = tagMappings; this.quoteNecessaryStrings = quoteNecessaryStrings; var specialStringValuePattern = quoteYaml1_1Strings @@ -94,16 +88,24 @@ public TypeAssigningEventEmitter(IEventEmitter nextEmitter, bool requireTagWhenS #endif } - public TypeAssigningEventEmitter(IEventEmitter nextEmitter, bool requireTagWhenStaticAndActualTypesAreDifferent, IDictionary tagMappings, bool quoteNecessaryStrings) - : this(nextEmitter, requireTagWhenStaticAndActualTypesAreDifferent, tagMappings) + public TypeAssigningEventEmitter(IEventEmitter nextEmitter, bool requireTagWhenStaticAndActualTypesAreDifferent, IDictionary tagMappings, bool quoteNecessaryStrings, bool quoteYaml1_1Strings, ScalarStyle defaultScalarStyle) + : this(nextEmitter, requireTagWhenStaticAndActualTypesAreDifferent, tagMappings, quoteNecessaryStrings, quoteYaml1_1Strings, defaultScalarStyle, YamlFormatter.Default) { - this.quoteNecessaryStrings = quoteNecessaryStrings; + } -#if NET40 - isSpecialStringValue_Regex = new Regex(SpecialStrings_Pattern); -#else - isSpecialStringValue_Regex = new Regex(SpecialStrings_Pattern, RegexOptions.Compiled); -#endif + public TypeAssigningEventEmitter(IEventEmitter nextEmitter, bool requireTagWhenStaticAndActualTypesAreDifferent, IDictionary tagMappings, bool quoteNecessaryStrings, ScalarStyle defaultScalarStyle) + : this(nextEmitter, requireTagWhenStaticAndActualTypesAreDifferent, tagMappings, quoteNecessaryStrings, false, defaultScalarStyle, YamlFormatter.Default) + { + } + + public TypeAssigningEventEmitter(IEventEmitter nextEmitter, bool requireTagWhenStaticAndActualTypesAreDifferent, IDictionary tagMappings, bool quoteNecessaryStrings, bool quoteYaml1_1Strings) + : this(nextEmitter, requireTagWhenStaticAndActualTypesAreDifferent, tagMappings, quoteNecessaryStrings, quoteYaml1_1Strings, ScalarStyle.Any) + { + } + + public TypeAssigningEventEmitter(IEventEmitter nextEmitter, bool requireTagWhenStaticAndActualTypesAreDifferent, IDictionary tagMappings, bool quoteNecessaryStrings) + : this(nextEmitter, requireTagWhenStaticAndActualTypesAreDifferent, tagMappings, quoteNecessaryStrings, false) + { } public TypeAssigningEventEmitter(IEventEmitter nextEmitter, bool requireTagWhenStaticAndActualTypesAreDifferent, IDictionary tagMappings) @@ -111,6 +113,7 @@ public TypeAssigningEventEmitter(IEventEmitter nextEmitter, bool requireTagWhenS { this.requireTagWhenStaticAndActualTypesAreDifferent = requireTagWhenStaticAndActualTypesAreDifferent; this.tagMappings = tagMappings ?? throw new ArgumentNullException(nameof(tagMappings)); + formatter = YamlFormatter.Default; } public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) @@ -130,7 +133,7 @@ public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) { case TypeCode.Boolean: eventInfo.Tag = JsonSchema.Tags.Bool; - eventInfo.RenderedValue = YamlFormatter.FormatBoolean(value); + eventInfo.RenderedValue = formatter.FormatBoolean(value); break; case TypeCode.Byte: @@ -159,23 +162,23 @@ public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) else { eventInfo.Tag = JsonSchema.Tags.Int; - eventInfo.RenderedValue = YamlFormatter.FormatNumber(value); + eventInfo.RenderedValue = formatter.FormatNumber(value); } break; case TypeCode.Single: eventInfo.Tag = JsonSchema.Tags.Float; - eventInfo.RenderedValue = YamlFormatter.FormatNumber((float)value); + eventInfo.RenderedValue = formatter.FormatNumber((float)value); break; case TypeCode.Double: eventInfo.Tag = JsonSchema.Tags.Float; - eventInfo.RenderedValue = YamlFormatter.FormatNumber((double)value); + eventInfo.RenderedValue = formatter.FormatNumber((double)value); break; case TypeCode.Decimal: eventInfo.Tag = JsonSchema.Tags.Float; - eventInfo.RenderedValue = YamlFormatter.FormatNumber(value); + eventInfo.RenderedValue = formatter.FormatNumber(value); break; case TypeCode.String: @@ -196,7 +199,7 @@ public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) case TypeCode.DateTime: eventInfo.Tag = DefaultSchema.Tags.Timestamp; - eventInfo.RenderedValue = YamlFormatter.FormatDateTime(value); + eventInfo.RenderedValue = formatter.FormatDateTime(value); break; case TypeCode.Empty: @@ -207,7 +210,7 @@ public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) default: if (eventInfo.Source.Type == typeof(TimeSpan)) { - eventInfo.RenderedValue = YamlFormatter.FormatTimeSpan(value); + eventInfo.RenderedValue = formatter.FormatTimeSpan(value); break; } diff --git a/YamlDotNet/Serialization/NodeDeserializers/ScalarNodeDeserializer.cs b/YamlDotNet/Serialization/NodeDeserializers/ScalarNodeDeserializer.cs index 7395fd37..79d3db69 100644 --- a/YamlDotNet/Serialization/NodeDeserializers/ScalarNodeDeserializer.cs +++ b/YamlDotNet/Serialization/NodeDeserializers/ScalarNodeDeserializer.cs @@ -35,13 +35,20 @@ public sealed class ScalarNodeDeserializer : INodeDeserializer private const string BooleanFalsePattern = "^(false|n|no|off)$"; private readonly bool attemptUnknownTypeDeserialization; private readonly ITypeConverter typeConverter; + private readonly YamlFormatter formatter; - public ScalarNodeDeserializer(bool attemptUnknownTypeDeserialization, ITypeConverter typeConverter) + public ScalarNodeDeserializer(bool attemptUnknownTypeDeserialization, ITypeConverter typeConverter, YamlFormatter formatter) { this.attemptUnknownTypeDeserialization = attemptUnknownTypeDeserialization; this.typeConverter = typeConverter ?? throw new ArgumentNullException(nameof(typeConverter)); + this.formatter = formatter; } + //public ScalarNodeDeserializer(bool attemptUnknownTypeDeserialization, ITypeConverter typeConverter) + // : this(attemptUnknownTypeDeserialization, typeConverter, YamlFormatter.Default) + //{ + //} + public bool Deserialize(IParser parser, Type expectedType, Func nestedObjectDeserializer, out object? value) { if (!parser.TryConsume(out var scalar)) @@ -78,15 +85,15 @@ public bool Deserialize(IParser parser, Type expectedType, Func byte.Parse(v), out result)) { } - else if (TryAndSwallow(() => short.Parse(v), out result)) { } - else if (TryAndSwallow(() => int.Parse(v), out result)) { } - else if (TryAndSwallow(() => long.Parse(v), out result)) { } - else if (TryAndSwallow(() => ulong.Parse(v), out result)) { } - else if (TryAndSwallow(() => float.Parse(v), out result)) { } - else if (TryAndSwallow(() => double.Parse(v), out result)) { } + if (TryAndSwallow(() => byte.Parse(v, formatter.NumberFormat), out result)) { } + else if (TryAndSwallow(() => short.Parse(v, formatter.NumberFormat), out result)) { } + else if (TryAndSwallow(() => int.Parse(v, formatter.NumberFormat), out result)) { } + else if (TryAndSwallow(() => long.Parse(v, formatter.NumberFormat), out result)) { } + else if (TryAndSwallow(() => ulong.Parse(v, formatter.NumberFormat), out result)) { } + else if (TryAndSwallow(() => float.Parse(v, formatter.NumberFormat), out result)) { } + else if (TryAndSwallow(() => double.Parse(v, formatter.NumberFormat), out result)) { } else { //we couldn't parse it, default to string, It's probably too big diff --git a/YamlDotNet/Serialization/SerializerBuilder.cs b/YamlDotNet/Serialization/SerializerBuilder.cs index e88eb9b1..df462672 100755 --- a/YamlDotNet/Serialization/SerializerBuilder.cs +++ b/YamlDotNet/Serialization/SerializerBuilder.cs @@ -59,6 +59,7 @@ public sealed class SerializerBuilder : BuilderSkeleton private int maximumRecursion = 50; private EmitterSettings emitterSettings = EmitterSettings.Default; private DefaultValuesHandling defaultValuesHandlingConfiguration = DefaultValuesHandling.Preserve; + private ScalarStyle defaultScalarStyle = ScalarStyle.Any; private bool quoteNecessaryStrings; private bool quoteYaml1_1Strings; @@ -97,7 +98,7 @@ public SerializerBuilder() eventEmitterFactories = new LazyComponentRegistrationList { - { typeof(TypeAssigningEventEmitter), inner => new TypeAssigningEventEmitter(inner, false, tagMappings, quoteNecessaryStrings, quoteYaml1_1Strings) } + { typeof(TypeAssigningEventEmitter), inner => new TypeAssigningEventEmitter(inner, false, tagMappings, quoteNecessaryStrings, quoteYaml1_1Strings, defaultScalarStyle, yamlFormatter) } }; objectFactory = new DefaultObjectFactory(); @@ -124,7 +125,8 @@ public SerializerBuilder WithQuotingNecessaryStrings(bool quoteYaml1_1Strings = /// public SerializerBuilder WithDefaultScalarStyle(ScalarStyle style) { - return WithEventEmitter(inner => new TypeAssigningEventEmitter(inner, false, tagMappings, quoteNecessaryStrings, quoteYaml1_1Strings, style), loc => loc.InsteadOf()); + this.defaultScalarStyle = style; + return this; } /// @@ -280,7 +282,7 @@ public SerializerBuilder EnsureRoundtrip() settings, objectFactory ); - WithEventEmitter(inner => new TypeAssigningEventEmitter(inner, true, tagMappings, quoteNecessaryStrings, quoteYaml1_1Strings), loc => loc.InsteadOf()); + WithEventEmitter(inner => new TypeAssigningEventEmitter(inner, true, tagMappings, quoteNecessaryStrings, quoteYaml1_1Strings, defaultScalarStyle, yamlFormatter), loc => loc.InsteadOf()); return WithTypeInspector(inner => new ReadableAndWritablePropertiesTypeInspector(inner), loc => loc.OnBottom()); } @@ -333,7 +335,7 @@ public SerializerBuilder JsonCompatible() return this .WithTypeConverter(new GuidConverter(true), w => w.InsteadOf()) .WithTypeConverter(new DateTimeConverter(doubleQuotes: true)) - .WithEventEmitter(inner => new JsonEventEmitter(inner), loc => loc.InsteadOf()); + .WithEventEmitter(inner => new JsonEventEmitter(inner, yamlFormatter), loc => loc.InsteadOf()); } /// diff --git a/YamlDotNet/Serialization/StaticBuilderSkeleton.cs b/YamlDotNet/Serialization/StaticBuilderSkeleton.cs index 2e471105..e789fd23 100644 --- a/YamlDotNet/Serialization/StaticBuilderSkeleton.cs +++ b/YamlDotNet/Serialization/StaticBuilderSkeleton.cs @@ -40,6 +40,7 @@ public abstract class StaticBuilderSkeleton internal readonly LazyComponentRegistrationList typeInspectorFactories; internal bool includeNonPublicProperties = false; internal Settings settings; + internal YamlFormatter yamlFormatter = YamlFormatter.Default; internal StaticBuilderSkeleton(ITypeResolver typeResolver) { @@ -238,6 +239,18 @@ public TBuilder WithoutTypeInspector(Type inspectorType) return Self; } + /// + /// Override the default yaml formatter with the one passed in + /// + /// to use when serializing and deserializing objects. + /// + /// + public TBuilder WithYamlFormatter(YamlFormatter formatter) + { + yamlFormatter = formatter ?? throw new ArgumentNullException(nameof(formatter)); + return Self; + } + protected IEnumerable BuildTypeConverters() { return typeConverterFactories.BuildComponentList(); diff --git a/YamlDotNet/Serialization/StaticDeserializerBuilder.cs b/YamlDotNet/Serialization/StaticDeserializerBuilder.cs index a763ff48..13b18596 100644 --- a/YamlDotNet/Serialization/StaticDeserializerBuilder.cs +++ b/YamlDotNet/Serialization/StaticDeserializerBuilder.cs @@ -87,7 +87,7 @@ public StaticDeserializerBuilder(StaticContext context) { typeof(YamlSerializableNodeDeserializer), _ => new YamlSerializableNodeDeserializer(factory) }, { typeof(TypeConverterNodeDeserializer), _ => new TypeConverterNodeDeserializer(BuildTypeConverters()) }, { typeof(NullNodeDeserializer), _ => new NullNodeDeserializer() }, - { typeof(ScalarNodeDeserializer), _ => new ScalarNodeDeserializer(attemptUnknownTypeDeserialization, typeConverter) }, + { typeof(ScalarNodeDeserializer), _ => new ScalarNodeDeserializer(attemptUnknownTypeDeserialization, typeConverter, yamlFormatter) }, { typeof(StaticArrayNodeDeserializer), _ => new StaticArrayNodeDeserializer(factory) }, { typeof(StaticDictionaryNodeDeserializer), _ => new StaticDictionaryNodeDeserializer(factory, duplicateKeyChecking) }, { typeof(StaticCollectionNodeDeserializer), _ => new StaticCollectionNodeDeserializer(factory) }, diff --git a/YamlDotNet/Serialization/StaticSerializerBuilder.cs b/YamlDotNet/Serialization/StaticSerializerBuilder.cs index 21eaea06..973c7a5b 100644 --- a/YamlDotNet/Serialization/StaticSerializerBuilder.cs +++ b/YamlDotNet/Serialization/StaticSerializerBuilder.cs @@ -57,6 +57,8 @@ public sealed class StaticSerializerBuilder : StaticBuilderSkeleton { - { typeof(TypeAssigningEventEmitter), inner => new TypeAssigningEventEmitter(inner, false, tagMappings, quoteNecessaryStrings) } + { typeof(TypeAssigningEventEmitter), inner => new TypeAssigningEventEmitter(inner, false, tagMappings, quoteNecessaryStrings, quoteYaml1_1Strings, defaultScalarStyle, yamlFormatter) } }; objectGraphTraversalStrategyFactory = (typeInspector, typeResolver, typeConverters, maximumRecursion) => @@ -104,6 +106,17 @@ public StaticSerializerBuilder(StaticContext context) protected override StaticSerializerBuilder Self { get { return this; } } + /// + /// Put double quotes around strings that need it, for example Null, True, False, a number. This should be called before any other "With" methods if you want this feature enabled. + /// + /// Also quote strings that are valid scalars in the YAML 1.1 specification (which includes boolean Yes/No/On/Off, base 60 numbers and more) + public StaticSerializerBuilder WithQuotingNecessaryStrings(bool quoteYaml1_1Strings = false) + { + quoteNecessaryStrings = true; + this.quoteYaml1_1Strings = quoteYaml1_1Strings; + return this; + } + /// /// Put double quotes around strings that need it, for example Null, True, False, a number. This should be called before any other "With" methods if you want this feature enabled. /// @@ -118,7 +131,8 @@ public StaticSerializerBuilder WithQuotingNecessaryStrings() /// public StaticSerializerBuilder WithDefaultScalarStyle(ScalarStyle style) { - return WithEventEmitter(inner => new TypeAssigningEventEmitter(inner, false, tagMappings, quoteNecessaryStrings, style), loc => loc.InsteadOf()); + this.defaultScalarStyle = style; + return this; } /// @@ -326,7 +340,7 @@ public StaticSerializerBuilder JsonCompatible() return this .WithTypeConverter(new GuidConverter(true), w => w.InsteadOf()) - .WithEventEmitter(inner => new JsonEventEmitter(inner), loc => loc.InsteadOf()); + .WithEventEmitter(inner => new JsonEventEmitter(inner, yamlFormatter), loc => loc.InsteadOf()); } /// diff --git a/YamlDotNet/Serialization/YamlFormatter.cs b/YamlDotNet/Serialization/YamlFormatter.cs index a91421db..16b3ae08 100644 --- a/YamlDotNet/Serialization/YamlFormatter.cs +++ b/YamlDotNet/Serialization/YamlFormatter.cs @@ -24,9 +24,11 @@ namespace YamlDotNet.Serialization { - internal static class YamlFormatter + public class YamlFormatter { - public static readonly NumberFormatInfo NumberFormat = new NumberFormatInfo + public static YamlFormatter Default { get; } = new YamlFormatter(); + + public NumberFormatInfo NumberFormat { get; set; } = new NumberFormatInfo { CurrencyDecimalSeparator = ".", CurrencyGroupSeparator = "_", @@ -42,32 +44,32 @@ internal static class YamlFormatter NegativeInfinitySymbol = "-.inf" }; - public static string FormatNumber(object number) + public string FormatNumber(object number) { return Convert.ToString(number, NumberFormat)!; } - public static string FormatNumber(double number) + public string FormatNumber(double number) { return number.ToString("G", NumberFormat); } - public static string FormatNumber(float number) + public string FormatNumber(float number) { return number.ToString("G", NumberFormat); } - public static string FormatBoolean(object boolean) + public string FormatBoolean(object boolean) { return boolean.Equals(true) ? "true" : "false"; } - public static string FormatDateTime(object dateTime) + public string FormatDateTime(object dateTime) { return ((DateTime)dateTime).ToString("o", CultureInfo.InvariantCulture); } - public static string FormatTimeSpan(object timeSpan) + public string FormatTimeSpan(object timeSpan) { return ((TimeSpan)timeSpan).ToString(); }