diff --git a/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs b/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs new file mode 100644 index 00000000000..34ed457ddd8 --- /dev/null +++ b/src/MongoDB.Bson/Serialization/Attributes/BsonDateOnlyOptionsAttribute.cs @@ -0,0 +1,69 @@ +/* Copyright 2010-present MongoDB Inc. +* +* Licensed under the Apache License, Version 2.0 (the "License"); +* you may not use this file except in compliance with the License. +* You may obtain a copy of the License at +* +* http://www.apache.org/licenses/LICENSE-2.0 +* +* Unless required by applicable law or agreed to in writing, software +* distributed under the License is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* See the License for the specific language governing permissions and +* limitations under the License. +*/ + +using System; +using MongoDB.Bson.Serialization.Options; +using MongoDB.Bson.Serialization.Serializers; + +namespace MongoDB.Bson.Serialization.Attributes +{ +#if NET6_0_OR_GREATER + /// + /// Specifies the external representation and related options for a DateOnly field or property. + /// + [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field)] + public class BsonDateOnlyOptionsAttribute : BsonSerializationOptionsAttribute + { + // private fields + private BsonType _representation; + private DateOnlyDocumentFormat _documentFormat; + + // constructors + + /// + /// Initializes a new instance of the BsonDateOnlyOptionsAttribute class. + /// + /// The external representation. + /// The format to use with document representation. + public BsonDateOnlyOptionsAttribute(BsonType representation, DateOnlyDocumentFormat documentDocumentFormat = DateOnlyDocumentFormat.DateTimeTicks) + { + _representation = representation; + _documentFormat = documentDocumentFormat; + } + + // public properties + /// + /// Gets the external representation. + /// + public BsonType Representation => _representation; + + /// + /// Gets the document format. + /// + public DateOnlyDocumentFormat DocumentFormat => _documentFormat; + + /// + /// Reconfigures the specified serializer by applying this attribute to it. + /// + /// The serializer. + /// A reconfigured serializer. + protected override IBsonSerializer Apply(IBsonSerializer serializer) + { + var reconfiguredSerializer = SerializerConfigurator.ReconfigureSerializer(serializer, (DateOnlySerializer s) => s.WithRepresentation(_representation, _documentFormat)); + return reconfiguredSerializer ?? base.Apply(serializer); + } + } +#endif +} diff --git a/src/MongoDB.Bson/Serialization/Options/DateOnlyDocumentFormat.cs b/src/MongoDB.Bson/Serialization/Options/DateOnlyDocumentFormat.cs new file mode 100644 index 00000000000..525635d1574 --- /dev/null +++ b/src/MongoDB.Bson/Serialization/Options/DateOnlyDocumentFormat.cs @@ -0,0 +1,33 @@ +/* Copyright 2010-present MongoDB Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +namespace MongoDB.Bson.Serialization.Options +{ + /// + /// Represents the format to use with a DateOnly serializer when the representation is BsonType.Document. + /// + public enum DateOnlyDocumentFormat + { + /// + /// The document will contain "DateTime" (BsonType.DateTime) and "Ticks" (BsonType.Int64). + /// + DateTimeTicks, + + /// + /// The document will contain "Year", "Month" and "Day" (all BsonType.Int32). + /// + YearMonthDay + } +} \ No newline at end of file diff --git a/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs new file mode 100644 index 00000000000..7e1cfbecbe5 --- /dev/null +++ b/src/MongoDB.Bson/Serialization/SerializerConfigurator.cs @@ -0,0 +1,50 @@ +/* Copyright 2010-present MongoDB Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +using System; + +namespace MongoDB.Bson.Serialization +{ + internal static class SerializerConfigurator + { + /// + /// Reconfigures a serializer using the specified method. + /// If the serializer implements , + /// the method traverses and applies the reconfiguration to its child serializers recursively until an appropriate leaf serializer is found. + /// + /// The input serializer to be reconfigured. + /// A function that defines how the serializer of type should be reconfigured. + /// The input type for the reconfigure method. + /// + /// The reconfigured serializer, or null if no leaf serializer could be reconfigured. + /// + internal static IBsonSerializer ReconfigureSerializer(IBsonSerializer serializer, Func reconfigure) + { + switch (serializer) + { + case IChildSerializerConfigurable childSerializerConfigurable: + var childSerializer = childSerializerConfigurable.ChildSerializer; + var reconfiguredChildSerializer = ReconfigureSerializer(childSerializer, reconfigure); + return reconfiguredChildSerializer != null? childSerializerConfigurable.WithChildSerializer(reconfiguredChildSerializer) : null; + + case TSerializer typedSerializer: + return reconfigure(typedSerializer); + + default: + return null; + } + } + } +} \ No newline at end of file diff --git a/src/MongoDB.Bson/Serialization/Serializers/DateOnlySerializer.cs b/src/MongoDB.Bson/Serialization/Serializers/DateOnlySerializer.cs index 85bffdf7e33..6dceaf65831 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/DateOnlySerializer.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/DateOnlySerializer.cs @@ -15,6 +15,7 @@ using System; using MongoDB.Bson.IO; +using MongoDB.Bson.Serialization.Attributes; using MongoDB.Bson.Serialization.Options; namespace MongoDB.Bson.Serialization.Serializers @@ -26,7 +27,7 @@ namespace MongoDB.Bson.Serialization.Serializers public sealed class DateOnlySerializer : StructSerializerBase, IRepresentationConfigurable { // static - private static readonly DateOnlySerializer __instance = new DateOnlySerializer(); + private static readonly DateOnlySerializer __instance = new(); /// /// Gets the default DateOnlySerializer. @@ -38,19 +39,26 @@ private static class Flags { public const long DateTime = 1; public const long Ticks = 2; + public const long Year = 4; + public const long Month = 8; + public const long Day = 16; + + public const long DateTimeTicks = DateTime | Ticks; + public const long YearMonthDay = Year | Month | Day; } // private fields private readonly RepresentationConverter _converter; private readonly SerializerHelper _helper; private readonly BsonType _representation; + private readonly DateOnlyDocumentFormat _documentFormat; // constructors /// /// Initializes a new instance of the class. /// public DateOnlySerializer() - : this(BsonType.DateTime) + : this(BsonType.DateTime, DateOnlyDocumentFormat.DateTimeTicks) { } @@ -59,6 +67,16 @@ public DateOnlySerializer() /// /// The representation. public DateOnlySerializer(BsonType representation) + : this(representation, DateOnlyDocumentFormat.DateTimeTicks) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// The representation. + /// The format to use with the BsonType.Document representation. It will be ignored if the representation is different. + public DateOnlySerializer(BsonType representation, DateOnlyDocumentFormat documentFormat) { switch (representation) { @@ -73,12 +91,15 @@ public DateOnlySerializer(BsonType representation) } _representation = representation; + _documentFormat = documentFormat; _converter = new RepresentationConverter(false, false); - _helper = new SerializerHelper ( - new SerializerHelper.Member("DateTime", Flags.DateTime), - new SerializerHelper.Member("Ticks", Flags.Ticks) + new SerializerHelper.Member("DateTime", Flags.DateTime, isOptional: true), + new SerializerHelper.Member("Ticks", Flags.Ticks, isOptional: true), + new SerializerHelper.Member("Year", Flags.Year, isOptional: true), + new SerializerHelper.Member("Month", Flags.Month, isOptional: true), + new SerializerHelper.Member("Day", Flags.Day, isOptional: true) ); } @@ -86,60 +107,68 @@ public DateOnlySerializer(BsonType representation) /// public BsonType Representation => _representation; + /// + /// The format to use for the BsonType.Document representation. It will be ignored if the representation is different. + /// + public DateOnlyDocumentFormat DocumentFormat => _documentFormat; + //public methods /// public override DateOnly Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { var bsonReader = context.Reader; - DateOnly value; var bsonType = bsonReader.GetCurrentBsonType(); switch (bsonType) { case BsonType.DateTime: - value = VerifyAndMakeDateOnly(BsonUtils.ToDateTimeFromMillisecondsSinceEpoch(bsonReader.ReadDateTime())); - break; + return VerifyAndMakeDateOnly(BsonUtils.ToDateTimeFromMillisecondsSinceEpoch(bsonReader.ReadDateTime())); case BsonType.Document: - value = default; - _helper.DeserializeMembers(context, (_, flag) => + var ticks = 0L; + var year = 0; + var month = 0; + var day = 0; + + var foundMemberFlags = _helper.DeserializeMembers(context, (_, flag) => { switch (flag) { - case Flags.DateTime: bsonReader.SkipValue(); break; // ignore value (use Ticks instead) - case Flags.Ticks: - value = VerifyAndMakeDateOnly(new DateTime(Int64Serializer.Instance.Deserialize(context), DateTimeKind.Utc)); - break; + case Flags.DateTime: bsonReader.SkipValue(); break; // ignore value (use Ticks instead) + case Flags.Ticks: ticks = Int64Serializer.Instance.Deserialize(context); break; + case Flags.Year: year = Int32Serializer.Instance.Deserialize(context); break; + case Flags.Month: month = Int32Serializer.Instance.Deserialize(context); break; + case Flags.Day: day = Int32Serializer.Instance.Deserialize(context); break; } }); - break; + + return foundMemberFlags switch + { + Flags.DateTimeTicks => VerifyAndMakeDateOnly(new DateTime(ticks, DateTimeKind.Utc)), + Flags.YearMonthDay => new DateOnly(year, month, day), + _ => throw new FormatException("Invalid document format.") + }; case BsonType.Decimal128: - value = VerifyAndMakeDateOnly(new DateTime(_converter.ToInt64(bsonReader.ReadDecimal128()), DateTimeKind.Utc)); - break; + return VerifyAndMakeDateOnly(new DateTime(_converter.ToInt64(bsonReader.ReadDecimal128()), DateTimeKind.Utc)); case BsonType.Double: - value = VerifyAndMakeDateOnly(new DateTime(_converter.ToInt64(bsonReader.ReadDouble()), DateTimeKind.Utc)); - break; + return VerifyAndMakeDateOnly(new DateTime(_converter.ToInt64(bsonReader.ReadDouble()), DateTimeKind.Utc)); case BsonType.Int32: - value = VerifyAndMakeDateOnly(new DateTime(bsonReader.ReadInt32(), DateTimeKind.Utc)); - break; + return VerifyAndMakeDateOnly(new DateTime(bsonReader.ReadInt32(), DateTimeKind.Utc)); case BsonType.Int64: - value = VerifyAndMakeDateOnly(new DateTime(bsonReader.ReadInt64(), DateTimeKind.Utc)); - break; + return VerifyAndMakeDateOnly(new DateTime(bsonReader.ReadInt64(), DateTimeKind.Utc)); case BsonType.String: - value = DateOnly.ParseExact(bsonReader.ReadString(), "yyyy-MM-dd"); - break; + return DateOnly.ParseExact(bsonReader.ReadString(), "yyyy-MM-dd"); default: throw CreateCannotDeserializeFromBsonTypeException(bsonType); } - return value; DateOnly VerifyAndMakeDateOnly(DateTime dt) { @@ -160,7 +189,8 @@ public override bool Equals(object obj) return base.Equals(obj) && obj is DateOnlySerializer other && - _representation.Equals(other._representation); + _representation.Equals(other._representation) && + _documentFormat.Equals(other._documentFormat); } /// @@ -182,8 +212,17 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati case BsonType.Document: bsonWriter.WriteStartDocument(); - bsonWriter.WriteDateTime("DateTime", millisecondsSinceEpoch); - bsonWriter.WriteInt64("Ticks", utcDateTime.Ticks); + if (_documentFormat is DateOnlyDocumentFormat.DateTimeTicks) + { + bsonWriter.WriteDateTime("DateTime", millisecondsSinceEpoch); + bsonWriter.WriteInt64("Ticks", utcDateTime.Ticks); + } + else + { + bsonWriter.WriteInt32("Year", value.Year); + bsonWriter.WriteInt32("Month", value.Month); + bsonWriter.WriteInt32("Day", value.Day); + } bsonWriter.WriteEndDocument(); break; @@ -200,10 +239,28 @@ public override void Serialize(BsonSerializationContext context, BsonSerializati } } + /// + /// Returns a serializer that has been reconfigured with the specified representation and document format. + /// + /// The representation. + /// The document format to use with BsonType.Document representation. + /// + /// The reconfigured serializer. + /// + public DateOnlySerializer WithRepresentation(BsonType representation, DateOnlyDocumentFormat documentFormat) + { + if (representation == _representation && documentFormat == _documentFormat) + { + return this; + } + + return new DateOnlySerializer(representation, documentFormat); + } + /// public DateOnlySerializer WithRepresentation(BsonType representation) { - return representation == _representation ? this : new DateOnlySerializer(representation); + return representation == _representation ? this : new DateOnlySerializer(representation, _documentFormat); } // explicit interface implementations diff --git a/src/MongoDB.Bson/Serialization/Serializers/SerializerHelper.cs b/src/MongoDB.Bson/Serialization/Serializers/SerializerHelper.cs index f9e9a6ccd79..81b43b0ce70 100644 --- a/src/MongoDB.Bson/Serialization/Serializers/SerializerHelper.cs +++ b/src/MongoDB.Bson/Serialization/Serializers/SerializerHelper.cs @@ -25,7 +25,6 @@ namespace MongoDB.Bson.Serialization.Serializers public class SerializerHelper { // private fields - private readonly long _allMemberFlags; private readonly long _extraMemberFlag; private readonly Member[] _members; private readonly long _requiredMemberFlags; @@ -52,7 +51,6 @@ public SerializerHelper(params Member[] members) foreach (var member in members) { - _allMemberFlags |= member.Flag; if (!member.IsOptional) { _requiredMemberFlags |= member.Flag; diff --git a/tests/MongoDB.Bson.Tests/Serialization/Serializers/DateOnlySerializerTests.cs b/tests/MongoDB.Bson.Tests/Serialization/Serializers/DateOnlySerializerTests.cs index b391a99fcd4..76b1159261c 100644 --- a/tests/MongoDB.Bson.Tests/Serialization/Serializers/DateOnlySerializerTests.cs +++ b/tests/MongoDB.Bson.Tests/Serialization/Serializers/DateOnlySerializerTests.cs @@ -19,6 +19,8 @@ using FluentAssertions; using MongoDB.Bson.IO; using MongoDB.Bson.Serialization; +using MongoDB.Bson.Serialization.Attributes; +using MongoDB.Bson.Serialization.Options; using MongoDB.Bson.Serialization.Serializers; using MongoDB.TestHelpers.XunitExtensions; using Xunit; @@ -28,6 +30,26 @@ namespace MongoDB.Bson.Tests.Serialization.Serializers #if NET6_0_OR_GREATER public class DateOnlySerializerTests { + [Fact] + public void Attribute_should_set_correct_format() + { + var dateOnly = new DateOnly(2024, 10, 05); + + var testObj = new TestClass + { + DateTimeTicksFormat = dateOnly, + YearMonthDayFormat = dateOnly, + IgnoredFormat = dateOnly, + ArrayYearMonthDayFormat = [dateOnly, dateOnly] + }; + + var json = testObj.ToJson(); + const string expected = """ + { "DateTimeTicksFormat" : { "DateTime" : { "$date" : "2024-10-05T00:00:00Z" }, "Ticks" : 638636832000000000 }, "YearMonthDayFormat" : { "Year" : 2024, "Month" : 10, "Day" : 5 }, "IgnoredFormat" : 638636832000000000, "ArrayYearMonthDayFormat" : [{ "Year" : 2024, "Month" : 10, "Day" : 5 }, { "Year" : 2024, "Month" : 10, "Day" : 5 }] } + """; + Assert.Equal(expected, json); + } + [Fact] public void Constructor_with_no_arguments_should_return_expected_result() { @@ -47,6 +69,20 @@ public void Constructor_with_representation_should_return_expected_result( subject.Representation.Should().Be(representation); } + [Theory] + [ParameterAttributeData] + public void Constructor_with_representation_and_format_should_return_expected_result( + [Values(BsonType.DateTime, BsonType.String, BsonType.Int64, BsonType.Document)] + BsonType representation, + [Values(DateOnlyDocumentFormat.DateTimeTicks, DateOnlyDocumentFormat.YearMonthDay)] + DateOnlyDocumentFormat format) + { + var subject = new DateOnlySerializer(representation, format); + + subject.Representation.Should().Be(representation); + subject.DocumentFormat.Should().Be(format); + } + [Theory] [InlineData("""{ "x" : { "$numberDouble" : "638649792000000000" } }""","2024-10-20" )] [InlineData("""{ "x" : { "$numberDecimal" : "638649792000000000" } }""","2024-10-20" )] @@ -81,6 +117,9 @@ public void Deserialize_should_be_forgiving_of_actual_numeric_types(string json, [InlineData("""{ "x" : { "DateTime" : { "$date" : { "$numberLong" : "1729382400000" } }, "Ticks" : { "$numberLong" : "638649792000000000" } } }""","2024-10-20" )] [InlineData("""{ "x" : { "DateTime" : { "$date" : { "$numberLong" : "-62135596800000" } }, "Ticks" : { "$numberLong" : "0" } } }""","0001-01-01" )] [InlineData("""{ "x" : { "DateTime" : { "$date" : { "$numberLong" : "253402214400000" } }, "Ticks" : { "$numberLong" : "3155378112000000000" } } }""","9999-12-31" )] + [InlineData("""{ "x" : { "Year" : 2024, "Month" : 10, "Day" : 5 } }""","2024-10-05" )] + [InlineData("""{ "x" : { "Year" : 9999, "Month" : 12, "Day" : 31 } }""","9999-12-31" )] + [InlineData("""{ "x" : { "Year" : 1, "Month" : 1, "Day" : 1 } }""","0001-01-01" )] public void Deserialize_should_have_expected_result(string json, string expectedResult) { var subject = new DateOnlySerializer(); @@ -95,6 +134,27 @@ public void Deserialize_should_have_expected_result(string json, string expected result.Should().Be(DateOnly.Parse(expectedResult, CultureInfo.InvariantCulture)); } + [Theory] + [InlineData("""{ "x" : { "Year" : 2024, "Month" : 10, "Day" : 5 } }""","2024-10-05" )] + [InlineData("""{ "x" : { "Year" : 9999, "Month" : 12, "Day" : 31 } }""","9999-12-31" )] + [InlineData("""{ "x" : { "Year" : 1, "Month" : 1, "Day" : 1 } }""","0001-01-01" )] + [InlineData("""{ "x" : { "DateTime" : { "$date" : { "$numberLong" : "1729382400000" } }, "Ticks" : { "$numberLong" : "638649792000000000" } } }""","2024-10-20" )] + [InlineData("""{ "x" : { "DateTime" : { "$date" : { "$numberLong" : "-62135596800000" } }, "Ticks" : { "$numberLong" : "0" } } }""","0001-01-01" )] + [InlineData("""{ "x" : { "DateTime" : { "$date" : { "$numberLong" : "253402214400000" } }, "Ticks" : { "$numberLong" : "3155378112000000000" } } }""","9999-12-31" )] + public void Deserialize_with_human_readable_should_have_expected_result(string json, string expectedResult) + { + var subject = new DateOnlySerializer(BsonType.Document, DateOnlyDocumentFormat.YearMonthDay); + + using var reader = new JsonReader(json); + reader.ReadStartDocument(); + reader.ReadName("x"); + var context = BsonDeserializationContext.CreateRoot(reader); + var result = subject.Deserialize(context); + reader.ReadEndDocument(); + + result.Should().Be(DateOnly.Parse(expectedResult, CultureInfo.InvariantCulture)); + } + [Theory] [InlineData("""{ "x" : { "$date" : { "$numberLong" : "1729382410000" } } }""")] [InlineData("""{ "x" : { "$numberLong" : "638649792100000000" } }""")] @@ -113,6 +173,22 @@ public void Deserialize_should_throw_when_date_has_time(string json) exception.Message.Should().Be("Deserialized value has a non-zero time component."); } + [Theory] + [InlineData("""{ "x" : { "Year": 2024, "DateTime" : { "$date" : { "$numberLong" : "1729382400000" } }, "Ticks" : { "$numberLong" : "638649792100000000" } } }""")] + public void Deserialize_should_throw_when_document_format_is_invalid(string json) + { + var subject = new DateOnlySerializer(); + + using var reader = new JsonReader(json); + reader.ReadStartDocument(); + reader.ReadName("x"); + var context = BsonDeserializationContext.CreateRoot(reader); + + var exception = Record.Exception(() => subject.Deserialize(context)); + exception.Should().BeOfType(); + exception.Message.Should().Be("Invalid document format."); + } + [Fact] public void Equals_null_should_return_false() { @@ -156,17 +232,28 @@ public void Equals_with_equal_fields_should_return_true() } [Theory] - [InlineData(BsonType.String)] - [InlineData(BsonType.Int64)] - [InlineData(BsonType.Document)] - public void Equals_with_not_equal_fields_should_return_true(BsonType representation) + [ParameterAttributeData] + public void Equals_with_different_representation_and_format_should_return_correct( + [Values(BsonType.DateTime, BsonType.String, BsonType.Int64, BsonType.Document)] + BsonType representation, + [Values(DateOnlyDocumentFormat.DateTimeTicks, DateOnlyDocumentFormat.YearMonthDay)] + DateOnlyDocumentFormat format) { var x = new DateOnlySerializer(); - var y = new DateOnlySerializer(representation); + var y = new DateOnlySerializer(representation, format); var result = x.Equals(y); + var result2 = y.Equals(x); + result.Should().Be(result2); - result.Should().Be(false); + if (representation == BsonType.DateTime && format == DateOnlyDocumentFormat.DateTimeTicks) + { + result.Should().Be(true); + } + else + { + result.Should().Be(false); + } } [Fact] @@ -220,6 +307,29 @@ public void Serialize_should_have_expected_result(BsonType representation, strin result.Should().Be(expectedResult); } + [Theory] + [InlineData("2024-10-20", """{ "x" : { "Year" : { "$numberInt" : "2024" }, "Month" : { "$numberInt" : "10" }, "Day" : { "$numberInt" : "20" } } }""")] + [InlineData( "0001-01-01", """{ "x" : { "Year" : { "$numberInt" : "1" }, "Month" : { "$numberInt" : "1" }, "Day" : { "$numberInt" : "1" } } }""")] + [InlineData("9999-12-31", """{ "x" : { "Year" : { "$numberInt" : "9999" }, "Month" : { "$numberInt" : "12" }, "Day" : { "$numberInt" : "31" } } }""")] + public void Serialize_human_readable_should_have_expected_result(string valueString, string expectedResult) + { + var subject = new DateOnlySerializer(BsonType.Document, DateOnlyDocumentFormat.YearMonthDay); + var value = DateOnly.Parse(valueString, CultureInfo.InvariantCulture); + + using var textWriter = new StringWriter(); + using var writer = new JsonWriter(textWriter, + new JsonWriterSettings { OutputMode = JsonOutputMode.CanonicalExtendedJson }); + + var context = BsonSerializationContext.CreateRoot(writer); + writer.WriteStartDocument(); + writer.WriteName("x"); + subject.Serialize(context, value); + writer.WriteEndDocument(); + var result = textWriter.ToString(); + + result.Should().Be(expectedResult); + } + [Fact] public void Serializer_should_be_registered() { @@ -244,6 +354,21 @@ public void WithRepresentation_should_return_expected_result( result.Should().BeSameAs(subject); } } + + private class TestClass + { + [BsonDateOnlyOptions(BsonType.Document, DateOnlyDocumentFormat.DateTimeTicks)] + public DateOnly DateTimeTicksFormat { get; set; } + + [BsonDateOnlyOptions(BsonType.Document, DateOnlyDocumentFormat.YearMonthDay)] + public DateOnly YearMonthDayFormat { get; set; } + + [BsonDateOnlyOptions(BsonType.Int64, DateOnlyDocumentFormat.YearMonthDay)] + public DateOnly IgnoredFormat { get; set; } + + [BsonDateOnlyOptions(BsonType.Document, DateOnlyDocumentFormat.YearMonthDay)] + public DateOnly[] ArrayYearMonthDayFormat { get; set; } + } } #endif } \ No newline at end of file