diff --git a/YamlDotNet/Serialization/Converters/DateOnlyConverter.cs b/YamlDotNet/Serialization/Converters/DateOnlyConverter.cs index 5f1a90ed..16bafc39 100644 --- a/YamlDotNet/Serialization/Converters/DateOnlyConverter.cs +++ b/YamlDotNet/Serialization/Converters/DateOnlyConverter.cs @@ -32,7 +32,7 @@ namespace YamlDotNet.Serialization.Converters /// /// This represents the YAML converter entity for . /// - public class DateOnlyConverter : IYamlTypeConverter + public class DateOnlyConverter : ScalarConverterBase { private readonly IFormatProvider provider; private readonly bool doubleQuotes; @@ -52,16 +52,6 @@ public DateOnlyConverter(IFormatProvider? provider = null, bool doubleQuotes = f this.formats = formats.DefaultIfEmpty("d").ToArray(); } - /// - /// Gets a value indicating whether the current converter supports converting the specified type. - /// - /// to check. - /// Returns True, if the current converter supports; otherwise returns False. - public bool Accepts(Type type) - { - return type == typeof(DateOnly); - } - /// /// Reads an object's state from a YAML parser. /// @@ -70,9 +60,9 @@ public bool Accepts(Type type) /// The deserializer to use to deserialize complex types. /// Returns the instance converted. /// On deserializing, all formats in the list are used for conversion. - public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + public override object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { - var value = parser.Consume().Value; + var value = ConsumeScalarValue(parser); var dateOnly = DateOnly.ParseExact(value, this.formats, this.provider); return dateOnly; @@ -86,12 +76,12 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria /// to convert. /// The root serializer that can be used to serialize complex types. /// On serializing, the first format in the list is used. - public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) + public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) { var dateOnly = (DateOnly)value!; - var formatted = dateOnly.ToString(this.formats.First(), this.provider); // Always take the first format of the list. + var formatted = dateOnly.ToString(this.formats.First(), this.provider); - emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, formatted, doubleQuotes ? ScalarStyle.DoubleQuoted : ScalarStyle.Any, true, false)); + EmitScalar(emitter, formatted, doubleQuotes ? ScalarStyle.DoubleQuoted : ScalarStyle.Any); } } } diff --git a/YamlDotNet/Serialization/Converters/DateTime8601Converter.cs b/YamlDotNet/Serialization/Converters/DateTime8601Converter.cs index 6d42f501..af302838 100644 --- a/YamlDotNet/Serialization/Converters/DateTime8601Converter.cs +++ b/YamlDotNet/Serialization/Converters/DateTime8601Converter.cs @@ -29,7 +29,7 @@ namespace YamlDotNet.Serialization.Converters /// /// This represents the YAML converter entity for using the ISO-8601 standard format. /// - public class DateTime8601Converter : IYamlTypeConverter + public class DateTime8601Converter : ScalarConverterBase { private readonly ScalarStyle scalarStyle; @@ -49,16 +49,6 @@ public DateTime8601Converter(ScalarStyle scalarStyle) this.scalarStyle = scalarStyle; } - /// - /// Gets a value indicating whether the current converter supports converting the specified type. - /// - /// to check. - /// Returns True, if the current converter supports; otherwise returns False. - public bool Accepts(Type type) - { - return type == typeof(DateTime); - } - /// /// Reads an object's state from a YAML parser. /// @@ -67,9 +57,9 @@ public bool Accepts(Type type) /// The deserializer to use to deserialize complex types. /// Returns the instance converted. /// On deserializing, all formats in the list are used for conversion. - public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + public override object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { - var value = parser.Consume().Value; + var value = ConsumeScalarValue(parser); var result = DateTime.ParseExact(value, "O", CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind); return result; @@ -83,11 +73,11 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria /// to convert. /// A serializer to serializer complext objects. /// On serializing, the first format in the list is used. - public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) + public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) { var formatted = ((DateTime)value!).ToString("O", CultureInfo.InvariantCulture); - emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, formatted, scalarStyle, true, false)); + EmitScalar(emitter, formatted, scalarStyle); } } } diff --git a/YamlDotNet/Serialization/Converters/DateTimeConverter.cs b/YamlDotNet/Serialization/Converters/DateTimeConverter.cs index d70f70b1..1c71cbf1 100644 --- a/YamlDotNet/Serialization/Converters/DateTimeConverter.cs +++ b/YamlDotNet/Serialization/Converters/DateTimeConverter.cs @@ -31,7 +31,7 @@ namespace YamlDotNet.Serialization.Converters /// /// This represents the YAML converter entity for . /// - public class DateTimeConverter : IYamlTypeConverter + public class DateTimeConverter : ScalarConverterBase { private readonly DateTimeKind kind; private readonly IFormatProvider provider; @@ -54,16 +54,6 @@ public DateTimeConverter(DateTimeKind kind = DateTimeKind.Utc, IFormatProvider? this.formats = formats.DefaultIfEmpty("G").ToArray(); } - /// - /// Gets a value indicating whether the current converter supports converting the specified type. - /// - /// to check. - /// Returns True, if the current converter supports; otherwise returns False. - public bool Accepts(Type type) - { - return type == typeof(DateTime); - } - /// /// Reads an object's state from a YAML parser. /// @@ -72,9 +62,9 @@ public bool Accepts(Type type) /// The deserializer to use to deserialize complex types. /// Returns the instance converted. /// On deserializing, all formats in the list are used for conversion. - public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + public override object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { - var value = parser.Consume().Value; + var value = ConsumeScalarValue(parser); var style = this.kind == DateTimeKind.Local ? DateTimeStyles.AssumeLocal : DateTimeStyles.AssumeUniversal; var dt = DateTime.ParseExact(value, this.formats, this.provider, style); @@ -90,13 +80,13 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria /// to convert. /// A serializer to serializer complext objects. /// On serializing, the first format in the list is used. - public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) + public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) { var dt = (DateTime)value!; var adjusted = this.kind == DateTimeKind.Local ? dt.ToLocalTime() : dt.ToUniversalTime(); - var formatted = adjusted.ToString(this.formats.First(), this.provider); // Always take the first format of the list. + var formatted = adjusted.ToString(this.formats.First(), this.provider); - emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, formatted, doubleQuotes ? ScalarStyle.DoubleQuoted : ScalarStyle.Any, true, false)); + EmitScalar(emitter, formatted, doubleQuotes ? ScalarStyle.DoubleQuoted : ScalarStyle.Any); } private static DateTime EnsureDateTimeKind(DateTime dt, DateTimeKind kind) diff --git a/YamlDotNet/Serialization/Converters/DateTimeOffsetConverter.cs b/YamlDotNet/Serialization/Converters/DateTimeOffsetConverter.cs index 80b5ccfe..09278859 100644 --- a/YamlDotNet/Serialization/Converters/DateTimeOffsetConverter.cs +++ b/YamlDotNet/Serialization/Converters/DateTimeOffsetConverter.cs @@ -32,7 +32,7 @@ namespace YamlDotNet.Serialization.Converters /// To use this converter, call WithTypeConverter(new DateTimeOffsetConverter()) on the /// or . /// - public class DateTimeOffsetConverter : IYamlTypeConverter + public class DateTimeOffsetConverter : ScalarConverterBase { private readonly IFormatProvider provider; private readonly ScalarStyle style; @@ -59,16 +59,6 @@ public DateTimeOffsetConverter( this.formats = formats.DefaultIfEmpty("O").ToArray(); } - /// - /// Gets a value indicating whether the current converter supports converting the specified type. - /// - /// to check. - /// Returns True, if the current converter supports; otherwise returns False. - public bool Accepts(Type type) - { - return type == typeof(DateTimeOffset); - } - /// /// Reads an object's state from a YAML parser. /// @@ -77,9 +67,9 @@ public bool Accepts(Type type) /// The deserializer to use to deserialize complex types. /// Returns the instance converted. /// On deserializing, all formats in the list are used for conversion. - public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + public override object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { - var value = parser.Consume().Value; + var value = ConsumeScalarValue(parser); var result = DateTimeOffset.ParseExact(value, formats, provider, dateStyle); return result; @@ -93,12 +83,12 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria /// to convert. /// A serializer to serializer complext objects. /// On serializing, the first format in the list is used. - public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) + public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) { var dt = (DateTimeOffset)value!; - var formatted = dt.ToString(formats.First(), this.provider); // Always take the first format of the list. + var formatted = dt.ToString(formats.First(), this.provider); - emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, formatted, style, true, false)); + EmitScalar(emitter, formatted, style); } } } diff --git a/YamlDotNet/Serialization/Converters/ScalarConverterBase.cs b/YamlDotNet/Serialization/Converters/ScalarConverterBase.cs new file mode 100644 index 00000000..dace2c69 --- /dev/null +++ b/YamlDotNet/Serialization/Converters/ScalarConverterBase.cs @@ -0,0 +1,63 @@ +// This file is part of YamlDotNet - A .NET library for YAML. +// Copyright (c) Antoine Aubry and contributors +// +// Permission is hereby granted, free of charge, to any person obtaining a copy of +// this software and associated documentation files (the "Software"), to deal in +// the Software without restriction, including without limitation the rights to +// use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies +// of the Software, and to permit persons to whom the Software is furnished to do +// so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. + +using System; +using YamlDotNet.Core; +using YamlDotNet.Core.Events; + +namespace YamlDotNet.Serialization.Converters +{ + /// + /// Base class for YAML type converters that handle a single scalar type. + /// Provides common functionality for type acceptance, scalar consumption, and emission. + /// + /// The type this converter handles. + public abstract class ScalarConverterBase : IYamlTypeConverter + { + /// + public bool Accepts(Type type) + { + return type == typeof(T); + } + + /// + public abstract object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer); + + /// + public abstract void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer); + + /// + /// Consumes and returns the scalar value from the parser. + /// + protected static string ConsumeScalarValue(IParser parser) + { + return parser.Consume().Value; + } + + /// + /// Emits a scalar value with the specified style. + /// + protected static void EmitScalar(IEmitter emitter, string formatted, ScalarStyle style) + { + emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, formatted, style, true, false)); + } + } +} diff --git a/YamlDotNet/Serialization/Converters/TimeOnlyConverter.cs b/YamlDotNet/Serialization/Converters/TimeOnlyConverter.cs index 437c634e..6efeebf8 100644 --- a/YamlDotNet/Serialization/Converters/TimeOnlyConverter.cs +++ b/YamlDotNet/Serialization/Converters/TimeOnlyConverter.cs @@ -32,7 +32,7 @@ namespace YamlDotNet.Serialization.Converters /// /// This represents the YAML converter entity for . /// - public class TimeOnlyConverter : IYamlTypeConverter + public class TimeOnlyConverter : ScalarConverterBase { private readonly IFormatProvider provider; private readonly bool doubleQuotes; @@ -52,16 +52,6 @@ public TimeOnlyConverter(IFormatProvider? provider = null, bool doubleQuotes = f this.formats = formats.DefaultIfEmpty("T").ToArray(); } - /// - /// Gets a value indicating whether the current converter supports converting the specified type. - /// - /// to check. - /// Returns True, if the current converter supports; otherwise returns False. - public bool Accepts(Type type) - { - return type == typeof(TimeOnly); - } - /// /// Reads an object's state from a YAML parser. /// @@ -70,9 +60,9 @@ public bool Accepts(Type type) /// The deserializer to use to deserialize complex types. /// Returns the instance converted. /// On deserializing, all formats in the list are used for conversion. - public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) + public override object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeserializer) { - var value = parser.Consume().Value; + var value = ConsumeScalarValue(parser); var timeOnly = TimeOnly.ParseExact(value, this.formats, this.provider); return timeOnly; @@ -86,12 +76,12 @@ public object ReadYaml(IParser parser, Type type, ObjectDeserializer rootDeseria /// to convert. /// A serializer to serializer complext objects. /// On serializing, the first format in the list is used. - public void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) + public override void WriteYaml(IEmitter emitter, object? value, Type type, ObjectSerializer serializer) { var timeOnly = (TimeOnly)value!; - var formatted = timeOnly.ToString(this.formats.First(), this.provider); // Always take the first format of the list. + var formatted = timeOnly.ToString(this.formats.First(), this.provider); - emitter.Emit(new Scalar(AnchorName.Empty, TagName.Empty, formatted, doubleQuotes ? ScalarStyle.DoubleQuoted : ScalarStyle.Any, true, false)); + EmitScalar(emitter, formatted, doubleQuotes ? ScalarStyle.DoubleQuoted : ScalarStyle.Any); } } } diff --git a/YamlDotNet/Serialization/EventEmitters/JsonEventEmitter.cs b/YamlDotNet/Serialization/EventEmitters/JsonEventEmitter.cs index 409417dd..53e36bd2 100644 --- a/YamlDotNet/Serialization/EventEmitters/JsonEventEmitter.cs +++ b/YamlDotNet/Serialization/EventEmitters/JsonEventEmitter.cs @@ -21,7 +21,6 @@ using System; using System.Globalization; -using System.Text.RegularExpressions; using YamlDotNet.Core; using YamlDotNet.Core.Events; using YamlDotNet.Serialization.NamingConventions; @@ -87,26 +86,33 @@ public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) case TypeCode.Single: var floatValue = (float)value; - eventInfo.RenderedValue = floatValue.ToString("G", CultureInfo.InvariantCulture); if (float.IsNaN(floatValue) || float.IsInfinity(floatValue)) { + eventInfo.RenderedValue = floatValue.ToString(CultureInfo.InvariantCulture); eventInfo.Style = ScalarStyle.DoubleQuoted; } + else + { + eventInfo.RenderedValue = formatter.FormatNumber(floatValue); + } break; case TypeCode.Double: var doubleValue = (double)value; - eventInfo.RenderedValue = doubleValue.ToString("G", CultureInfo.InvariantCulture); if (double.IsNaN(doubleValue) || double.IsInfinity(doubleValue)) { + eventInfo.RenderedValue = doubleValue.ToString(CultureInfo.InvariantCulture); eventInfo.Style = ScalarStyle.DoubleQuoted; } + else + { + eventInfo.RenderedValue = formatter.FormatNumber(doubleValue); + } break; case TypeCode.Decimal: - var decimalValue = (decimal)value; - eventInfo.RenderedValue = decimalValue.ToString(CultureInfo.InvariantCulture); + eventInfo.RenderedValue = formatter.FormatNumber(value); break; case TypeCode.String: diff --git a/YamlDotNet/Serialization/EventEmitters/TypeAssigningEventEmitter.cs b/YamlDotNet/Serialization/EventEmitters/TypeAssigningEventEmitter.cs index 5dae3fbf..2e61e139 100644 --- a/YamlDotNet/Serialization/EventEmitters/TypeAssigningEventEmitter.cs +++ b/YamlDotNet/Serialization/EventEmitters/TypeAssigningEventEmitter.cs @@ -129,7 +129,7 @@ public override void Emit(ScalarEventInfo eventInfo, IEmitter emitter) case TypeCode.UInt32: case TypeCode.UInt64: //Enum's are special cases, they fall in here, but get sent out as a string. - if (eventInfo.Source.Type.IsEnum) + if (eventInfo.Source.Type.IsEnum()) { eventInfo.Tag = FailsafeSchema.Tags.Str; eventInfo.RenderedValue = formatter.FormatEnum(value, typeInspector, enumNamingConvention);