Skip to content

Commit 3348916

Browse files
committed
Add support for DateOnly
Support is introduced via custom extensibility across all supported serializers. For readability, when the value can be read by a human, we serialize to ISO 8601 format ("O" or roundtrip), and as the DayNumber integrer otherwise. Fixes #78
1 parent 5e6e858 commit 3348916

15 files changed

+140
-20
lines changed

readme.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,14 @@ public record Product(string Category, string Id)
1919
{
2020
public string? Title { get; init; }
2121
public double Price { get; init; }
22+
public DateOnly Created { get; init; }
2223
}
2324
```
2425

2526
> NOTE: entity can have custom constructor, key properties can be read-only,
2627
> and it doesn't need to inherit from anything, implement any interfaces or use
2728
> any custom attributes (unless you want to). As shown above, it can even be
28-
> a simple record type!
29+
> a simple record type, with support for .NET 6 DateOnly type to boot!
2930
3031
The entity can be stored and retrieved with:
3132

src/TableStorage.Bson/BsonDocumentSerializer.cs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//<auto-generated/>
22
#nullable enable
3+
using System;
4+
using System.Globalization;
35
using System.IO;
46
using Newtonsoft.Json;
57

@@ -11,7 +13,18 @@ namespace Devlooped
1113
/// </summary>
1214
partial class BsonDocumentSerializer : IBinaryDocumentSerializer
1315
{
14-
static readonly JsonSerializer serializer = new JsonSerializer();
16+
static readonly JsonSerializer serializer =
17+
#if NET6_0_OR_GREATER
18+
JsonSerializer.Create(new JsonSerializerSettings
19+
{
20+
Converters =
21+
{
22+
new DateOnlyJsonConverter(),
23+
}
24+
});
25+
#else
26+
new JsonSerializer();
27+
#endif
1528

1629
/// <summary>
1730
/// Default instance of the serializer.
@@ -40,5 +53,16 @@ public byte[] Serialize<T>(T value)
4053
serializer.Serialize(writer, value);
4154
return mem.ToArray();
4255
}
56+
57+
#if NET6_0_OR_GREATER
58+
class DateOnlyJsonConverter : JsonConverter<DateOnly>
59+
{
60+
public override DateOnly ReadJson(JsonReader reader, Type objectType, DateOnly existingValue, bool hasExistingValue, JsonSerializer serializer)
61+
=> DateOnly.Parse((string)reader.Value, CultureInfo.InvariantCulture);
62+
63+
public override void WriteJson(JsonWriter writer, DateOnly value, JsonSerializer serializer)
64+
=> writer.WriteValue(value.ToString("O", CultureInfo.InvariantCulture));
65+
}
66+
#endif
4367
}
4468
}

src/TableStorage.Bson/TableStorage.Bson.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<AssemblyName>Devlooped.TableStorage.Bson</AssemblyName>
5-
<TargetFramework>netstandard2.0</TargetFramework>
5+
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
66
<IsPackable>true</IsPackable>
77
<Description>A BSON binary serializer for use with document-based repositories.
88

src/TableStorage.MessagePack/MessagePackDocumentSerializer.cs

Lines changed: 40 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
//<auto-generated/>
22
#nullable enable
3+
using System;
4+
using System.Collections.Generic;
35
using MessagePack;
6+
using MessagePack.Formatters;
7+
using MessagePack.Resolvers;
48

59
namespace Devlooped
610
{
@@ -20,12 +24,47 @@ partial class MessagePackDocumentSerializer : IBinaryDocumentSerializer
2024
/// <summary>
2125
/// Initializes the document serializer with the given optional serializer options.
2226
/// </summary>
23-
public MessagePackDocumentSerializer(MessagePackSerializerOptions? options = default) => this.options = options;
27+
public MessagePackDocumentSerializer(MessagePackSerializerOptions? options = default)
28+
{
29+
this.options = options;
30+
#if NET6_0_OR_GREATER
31+
this.options = (options ?? MessagePackSerializerOptions.Standard)
32+
.WithResolver(CompositeResolver.Create(
33+
StandardResolver.Instance,
34+
DateOnlyFormatterResolver.Instance));
35+
#endif
36+
}
2437

2538
/// <inheritdoc />
2639
public T? Deserialize<T>(byte[] data) => data.Length == 0 ? default : MessagePackSerializer.Deserialize<T>(data, options);
2740

2841
/// <inheritdoc />
2942
public byte[] Serialize<T>(T value) => value == null ? new byte[0] : MessagePackSerializer.Serialize(value.GetType(), value, options);
43+
44+
#if NET6_0_OR_GREATER
45+
class DateOnlyFormatterResolver : IFormatterResolver
46+
{
47+
public static IFormatterResolver Instance = new DateOnlyFormatterResolver();
48+
49+
public IMessagePackFormatter<T> GetFormatter<T>()
50+
{
51+
if (typeof(T) == typeof(DateOnly))
52+
return (IMessagePackFormatter<T>)DateOnlyFormatter.Instance;
53+
54+
return null!;
55+
}
56+
57+
class DateOnlyFormatter : IMessagePackFormatter<DateOnly>
58+
{
59+
public static readonly IMessagePackFormatter<DateOnly> Instance = new DateOnlyFormatter();
60+
61+
public void Serialize(ref MessagePackWriter writer, DateOnly value, MessagePackSerializerOptions options)
62+
=> writer.Write(value.DayNumber);
63+
64+
public DateOnly Deserialize(ref MessagePackReader reader, MessagePackSerializerOptions options)
65+
=> DateOnly.FromDayNumber(reader.ReadInt32());
66+
}
67+
}
68+
#endif
3069
}
3170
}

src/TableStorage.MessagePack/TableStorage.MessagePack.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<AssemblyName>Devlooped.TableStorage.MessagePack</AssemblyName>
5-
<TargetFramework>netstandard2.0</TargetFramework>
5+
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
66
<IsPackable>true</IsPackable>
77
<Description>A MessagePack binary serializer for use with document-based repositories.
88

src/TableStorage.Newtonsoft/JsonDocumentSerializer.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
//<auto-generated/>
22
#nullable enable
3+
using System;
34
using System.Globalization;
45
using Newtonsoft.Json;
56
using Newtonsoft.Json.Converters;
@@ -38,6 +39,9 @@ partial class JsonDocumentSerializer : IStringDocumentSerializer
3839
DateTimeFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK",
3940
DateTimeStyles = DateTimeStyles.AdjustToUniversal
4041
},
42+
#if NET6_0_OR_GREATER
43+
new DateOnlyJsonConverter(),
44+
#endif
4145
},
4246
DateFormatHandling = DateFormatHandling.IsoDateFormat,
4347
DateFormatString = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'fffK",
@@ -68,5 +72,16 @@ public JsonDocumentSerializer()
6872

6973
/// <inheritdoc />
7074
public string Serialize<T>(T value) => JsonConvert.SerializeObject(value, settings);
75+
76+
#if NET6_0_OR_GREATER
77+
class DateOnlyJsonConverter : JsonConverter<DateOnly>
78+
{
79+
public override DateOnly ReadJson(JsonReader reader, Type objectType, DateOnly existingValue, bool hasExistingValue, JsonSerializer serializer)
80+
=> DateOnly.Parse((string)reader.Value, CultureInfo.InvariantCulture);
81+
82+
public override void WriteJson(JsonWriter writer, DateOnly value, JsonSerializer serializer)
83+
=> writer.WriteValue(value.ToString("O", CultureInfo.InvariantCulture));
84+
}
85+
#endif
7186
}
7287
}

src/TableStorage.Newtonsoft/TableStorage.Newtonsoft.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<AssemblyName>Devlooped.TableStorage.Newtonsoft</AssemblyName>
5-
<TargetFramework>netstandard2.0</TargetFramework>
5+
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
66
<IsPackable>true</IsPackable>
77
<Description>A Newtonsoft.Json-based serializer for use with document-based repositories.
88

src/TableStorage.Protobuf/ProtobufDocumentSerializer.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
//<auto-generated/>
22
#nullable enable
3+
using System;
34
using System.IO;
45
using ProtoBuf;
6+
using ProtoBuf.Meta;
57

68
namespace Devlooped
79
{
@@ -16,6 +18,18 @@ partial class ProtobufDocumentSerializer : IBinaryDocumentSerializer
1618
/// </summary>
1719
public static IDocumentSerializer Default { get; } = new ProtobufDocumentSerializer();
1820

21+
#if NET6_0_OR_GREATER
22+
static ProtobufDocumentSerializer()
23+
{
24+
RuntimeTypeModel.Default.SetSurrogate<DateOnly, int>(
25+
UnderlyingToSurrogate,
26+
SurrogateToUnderlying);
27+
}
28+
29+
static int UnderlyingToSurrogate(DateOnly value) => value.DayNumber;
30+
static DateOnly SurrogateToUnderlying(int value) => DateOnly.FromDayNumber(value);
31+
#endif
32+
1933
/// <inheritdoc />
2034
public T? Deserialize<T>(byte[] data)
2135
{

src/TableStorage.Protobuf/TableStorage.Protobuf.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<AssemblyName>Devlooped.TableStorage.Protobuf</AssemblyName>
5-
<TargetFramework>netstandard2.0</TargetFramework>
5+
<TargetFrameworks>netstandard2.0;net6.0</TargetFrameworks>
66
<IsPackable>true</IsPackable>
77
<Description>A Protocol Buffers binary serializer for use with document-based repositories.
88

src/TableStorage/DocumentSerializer.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
//<auto-generated/>
22
#nullable enable
3+
using System;
4+
using System.Globalization;
35
using System.Text.Json;
46
using System.Text.Json.Serialization;
57

@@ -27,10 +29,24 @@ partial class DocumentSerializer : IStringDocumentSerializer
2729
Converters =
2830
{
2931
// Enums should persist/parse with their string values instead
30-
new JsonStringEnumConverter(allowIntegerValues: false)
32+
new JsonStringEnumConverter(allowIntegerValues: false),
33+
#if NET6_0_OR_GREATER
34+
new DateOnlyJsonConverter()
35+
#endif
3136
}
3237
};
3338

39+
#if NET6_0_OR_GREATER
40+
public class DateOnlyJsonConverter : JsonConverter<DateOnly>
41+
{
42+
public override DateOnly Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
43+
=> DateOnly.Parse(reader.GetString()?.Substring(0, 10) ?? "", CultureInfo.InvariantCulture);
44+
45+
public override void Write(Utf8JsonWriter writer, DateOnly value, JsonSerializerOptions options)
46+
=> writer.WriteStringValue(value.ToString("O", CultureInfo.InvariantCulture));
47+
}
48+
#endif
49+
3450
/// <summary>
3551
/// Default instance of the serializer.
3652
/// </summary>

0 commit comments

Comments
 (0)