diff --git a/Dockerfile.NonEnglish b/Dockerfile.NonEnglish index 5cb039aeb..4f5f4704b 100644 --- a/Dockerfile.NonEnglish +++ b/Dockerfile.NonEnglish @@ -38,17 +38,16 @@ 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 +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" @@ -65,12 +64,12 @@ RUN echo -n "${LC_ALL}" > /etc/locale.gen && \ locale-gen RUN dotnet test -c Release YamlDotNet.sln --framework net70 --logger:"trx;LogFileName=/output/tests.net70.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 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" +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 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 -#WORKDIR /libraries -#COPY --from=build /output /libraries -#CMD [ "cp", "-r", "/libraries", "/output" ] \ No newline at end of file +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 65f1146e8..6b07db503 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.Test/Serialization/DeserializerTest.cs b/YamlDotNet.Test/Serialization/DeserializerTest.cs index f715fd1e3..b62af24ae 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 097935793..278eb44b4 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 5fafb1454..d1faf54d7 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 3d96a7286..e79087091 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 2633ad5c7..2f2ec1809 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 7395fd373..79d3db69e 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 e88eb9b16..df4626725 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 2e4711050..e789fd234 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 a763ff48d..13b185967 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 21eaea06c..973c7a5b0 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 a91421db5..16b3ae08c 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(); }