Skip to content

Commit 03357b1

Browse files
Merge pull request #1270 from TransactionProcessing/task/#1262_remove_newtonsoft
Add new property formats and DateTimeSpaceConverter
2 parents 63b0dd3 + 8b1e31f commit 03357b1

1 file changed

Lines changed: 72 additions & 19 deletions

File tree

Shared/Serialisation/StringSerialiser.cs

Lines changed: 72 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using Microsoft.Extensions.Options;
33
using System;
44
using System.Collections.Generic;
5+
using System.Globalization;
56
using System.Linq;
67
using System.Text;
78
using System.Text.Json;
@@ -15,6 +16,9 @@ public enum SerialiserPropertyFormat
1516
{
1617
CamelCase,
1718
SnakeCase,
19+
CamelCaseUpper,
20+
KebabCase,
21+
KeabCaseUpper,
1822
}
1923
public record SerialiserOptions(SerialiserPropertyFormat PropertyFormat, Boolean IgnoreNullValues = true, Boolean WriteIndented = false);
2024

@@ -34,29 +38,30 @@ public class SystemTextJsonSerializer : IStringSerialiser
3438
{
3539
private readonly JsonSerializerOptions Options;
3640

37-
public static JsonSerializerOptions GetDefaultJsonSerializerOptions() => new JsonSerializerOptions
38-
{
39-
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
40-
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
41-
WriteIndented = true,
42-
TypeInfoResolver = new DefaultJsonTypeInfoResolver
43-
{
44-
Modifiers =
45-
{
46-
typeInfo =>
47-
{
48-
String[] names = new[] { "AggregateId", "AggregateVersion", "EventId", "EventNumber", "EventTimestamp", "EventType" };
49-
List<JsonPropertyInfo> matches = typeInfo.Properties
50-
.Where(p => names.Any(n => string.Equals(p.Name, n, StringComparison.OrdinalIgnoreCase)))
51-
.ToList();
41+
public static JsonSerializerOptions GetDefaultJsonSerializerOptions() {
42+
JsonSerializerOptions options = new JsonSerializerOptions() {
43+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault,
44+
PropertyNamingPolicy = JsonNamingPolicy.SnakeCaseLower,
45+
WriteIndented = true,
46+
NumberHandling = JsonNumberHandling.AllowReadingFromString,
47+
TypeInfoResolver = new DefaultJsonTypeInfoResolver {
48+
Modifiers = {
49+
typeInfo => {
50+
String[] names = new[] { "AggregateId", "AggregateVersion", "EventId", "EventNumber", "EventTimestamp", "EventType" };
51+
List<JsonPropertyInfo> matches = typeInfo.Properties.Where(p => names.Any(n => string.Equals(p.Name, n, StringComparison.OrdinalIgnoreCase))).ToList();
5252

53-
foreach (JsonPropertyInfo match in matches) {
54-
match.ShouldSerialize = (_, _) => false;
53+
foreach (JsonPropertyInfo match in matches) {
54+
match.ShouldSerialize = (_,
55+
_) => false;
56+
}
5557
}
5658
}
5759
}
58-
}
59-
};
60+
};
61+
options.Converters.Add(new DateTimeSpaceConverter());
62+
63+
return options;
64+
}
6065

6166
public SystemTextJsonSerializer(JsonSerializerOptions options) {
6267
Options = options;
@@ -81,6 +86,9 @@ private JsonSerializerOptions BuildSerialiserOptions(SerialiserOptions serialise
8186
{
8287
SerialiserPropertyFormat.CamelCase => JsonNamingPolicy.CamelCase,
8388
SerialiserPropertyFormat.SnakeCase => JsonNamingPolicy.SnakeCaseLower,
89+
SerialiserPropertyFormat.CamelCaseUpper => JsonNamingPolicy.SnakeCaseUpper,
90+
SerialiserPropertyFormat.KebabCase => JsonNamingPolicy.KebabCaseLower,
91+
SerialiserPropertyFormat.KeabCaseUpper => JsonNamingPolicy.KebabCaseUpper,
8492
_ => options.PropertyNamingPolicy
8593
};
8694
return options;
@@ -204,4 +212,49 @@ public static T GetValue<T>(String json, String propertyName, SerialiserOptions
204212
if (!IsInitialised) throw new InvalidOperationException(NotInitialisedErrorMessage);
205213
return Serializer.GetValue<T>(json, propertyName);
206214
}
215+
}
216+
217+
public class DateTimeSpaceConverter : JsonConverter<DateTime>
218+
{
219+
private static readonly string[] AcceptedFormats = new[] {
220+
"yyyy-MM-dd HH:mm:ss", "yyyy-MM-dd H:mm:ss", "yyyy-MM-ddTHH:mm:ss", "yyyy-MM-ddTHH:mm:ss.FFFFFFFK", "o" // ISO 8601 round-trip
221+
};
222+
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
223+
{
224+
if (reader.TokenType == JsonTokenType.Null)
225+
{
226+
return default;
227+
}
228+
229+
if (reader.TokenType == JsonTokenType.String)
230+
{
231+
var s = reader.GetString();
232+
if (string.IsNullOrWhiteSpace(s))
233+
return default;
234+
235+
// Try exact known formats first (handles "2026-05-07 06:03:18")
236+
if (DateTime.TryParseExact(s, AcceptedFormats, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal | DateTimeStyles.AllowWhiteSpaces, out var dtExact))
237+
return dtExact;
238+
239+
// Fall back to general parse
240+
if (DateTime.TryParse(s, CultureInfo.InvariantCulture, DateTimeStyles.AssumeLocal, out var dt))
241+
return dt;
242+
243+
throw new JsonException($"Unable to parse DateTime: '{s}'.");
244+
}
245+
246+
// If JSON contains a number, attempt to treat it as Unix seconds (optional)
247+
if (reader.TokenType == JsonTokenType.Number && reader.TryGetInt64(out long seconds))
248+
{
249+
return DateTimeOffset.FromUnixTimeSeconds(seconds).LocalDateTime;
250+
}
251+
252+
throw new JsonException($"Unexpected token parsing DateTime. Token: {reader.TokenType}");
253+
}
254+
255+
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
256+
{
257+
// Write in the same "space" format so round-trip matches your input
258+
writer.WriteStringValue(value.ToString("yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture));
259+
}
207260
}

0 commit comments

Comments
 (0)