Skip to content

Commit

Permalink
Separate writing property names out as a new method, for compatibility
Browse files Browse the repository at this point in the history
  • Loading branch information
jskeet committed Mar 10, 2024
1 parent e349d15 commit 080b304
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 42 deletions.
18 changes: 14 additions & 4 deletions src/NodaTime.Serialization.SystemTextJson/NodaConverterBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -115,16 +115,26 @@ public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions
/// <param name="value">The value to write.</param>
/// <param name="options">The serializer options to use for any embedded serialization.</param>
public override void WriteAsPropertyName(Utf8JsonWriter writer, T value, JsonSerializerOptions options) =>
WriteJsonImpl(writer, value, options, true);
WriteJsonPropertyNameImpl(writer, value, options);

/// <summary>
/// Implemented by concrete subclasses, this performs the final write operation for a non-null value of type T
/// to JSON.
/// </summary>
/// <param name="writer">The writer to write JSON data to</param>
/// <param name="value">The value to serializer</param>
/// <param name="value">The value to serialize</param>
/// <param name="options">The serializer options to use for nested serialization</param>
/// <param name="isProperty">Boolean flag to determine which writer method to invoke</param>
protected abstract void WriteJsonImpl(Utf8JsonWriter writer, T value, JsonSerializerOptions options, bool isProperty = false);
protected abstract void WriteJsonImpl(Utf8JsonWriter writer, T value, JsonSerializerOptions options);

/// <summary>
/// Implemented by concrete subclasses, this performs the final write operation for a non-null value of type T
/// to JSON, writing the value as a property name. The default implementation throws <see cref="NotImplementedException"/>
/// for compatibility purposes, but all concrete classes within this package override and implement the method fully.
/// </summary>
/// <param name="writer">The writer to write JSON data to</param>
/// <param name="value">The value to serialize</param>
/// <param name="options">The serializer options to use for nested serialization</param>
protected virtual void WriteJsonPropertyNameImpl(Utf8JsonWriter writer, T value, JsonSerializerOptions options) =>
throw new NotImplementedException();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,7 @@ protected override DateInterval ReadJsonImpl(ref Utf8JsonReader reader, JsonSeri
/// <param name="writer">The writer to write JSON to</param>
/// <param name="value">The date interval to serialize</param>
/// <param name="options">The serializer options for embedded serialization.</param>
/// <param name="isProperty">DateInterval cannot be converted to a scalar string value as such it is not a valid choice for a dictionary key</param>
protected override void WriteJsonImpl(Utf8JsonWriter writer, DateInterval value, JsonSerializerOptions options, bool isProperty = false)
protected override void WriteJsonImpl(Utf8JsonWriter writer, DateInterval value, JsonSerializerOptions options)
{
writer.WriteStartObject();

Expand All @@ -88,5 +87,15 @@ protected override void WriteJsonImpl(Utf8JsonWriter writer, DateInterval value,

writer.WriteEndObject();
}

/// <summary>
/// Unconditionally throws an exception, as a DateInterval cannot be serialized as a JSON property name.
/// </summary>
/// <param name="writer">The writer to write JSON to</param>
/// <param name="value">The date interval to serialize</param>
/// <param name="options">The serializer options for embedded serialization.</param>
/// <exception cref="InvalidOperationException">Always thrown to indicate this is not an appropriate method to call on this type.</exception>
protected override void WriteJsonPropertyNameImpl(Utf8JsonWriter writer, DateInterval value, JsonSerializerOptions options) =>
throw new JsonException("Cannot serialize a DateInterval as a JSON property name using this converter");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,14 +41,16 @@ protected override DateTimeZone ReadJsonImpl(ref Utf8JsonReader reader, JsonSeri
/// <param name="writer">The writer to write JSON data to.</param>
/// <param name="value">The value to serialize.</param>
/// <param name="options">The serialization options to use for nested serialization.</param>
/// <param name="isProperty">Conditional to indicate which function to invoke on the writer</param>
protected override void WriteJsonImpl(Utf8JsonWriter writer, DateTimeZone value, JsonSerializerOptions options,
bool isProperty = false)
{
if (isProperty)
writer.WritePropertyName(value.Id);
else
writer.WriteStringValue(value.Id);
}
protected override void WriteJsonImpl(Utf8JsonWriter writer, DateTimeZone value, JsonSerializerOptions options) =>
writer.WriteStringValue(value.Id);

/// <summary>
/// Writes the time zone ID to the writer as a property name
/// </summary>
/// <param name="writer">The writer to write JSON data to.</param>
/// <param name="value">The value to serialize.</param>
/// <param name="options">The serialization options to use for nested serialization.</param>
protected override void WriteJsonPropertyNameImpl(Utf8JsonWriter writer, DateTimeZone value, JsonSerializerOptions options) =>
writer.WritePropertyName(value.Id);
}
}
13 changes: 11 additions & 2 deletions src/NodaTime.Serialization.SystemTextJson/NodaIntervalConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ protected override Interval ReadJsonImpl(ref Utf8JsonReader reader, JsonSerializ
/// <param name="writer">The writer to write JSON to</param>
/// <param name="value">The interval to serialize</param>
/// <param name="options">The serializer options for embedded serialization.</param>
/// <param name="isProperty">Interval cannot be converted to a scalar string value as such it is not a valid choice for a dictionary key</param>
protected override void WriteJsonImpl(Utf8JsonWriter writer, Interval value, JsonSerializerOptions options, bool isProperty = false)
protected override void WriteJsonImpl(Utf8JsonWriter writer, Interval value, JsonSerializerOptions options)
{
writer.WriteStartObject();

Expand All @@ -83,5 +82,15 @@ protected override void WriteJsonImpl(Utf8JsonWriter writer, Interval value, Jso
}
writer.WriteEndObject();
}

/// <summary>
/// Unconditionally throws an exception, as an Interval cannot be serialized as a JSON property name.
/// </summary>
/// <param name="writer">The writer to write JSON to</param>
/// <param name="value">The date interval to serialize</param>
/// <param name="options">The serializer options for embedded serialization.</param>
/// <exception cref="InvalidOperationException">Always thrown to indicate this is not an appropriate method to call on this type.</exception>
protected override void WriteJsonPropertyNameImpl(Utf8JsonWriter writer, Interval value, JsonSerializerOptions options) =>
throw new JsonException("Cannot serialize an Interval as a JSON property name using this converter");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,15 +59,18 @@ protected override DateInterval ReadJsonImpl(ref Utf8JsonReader reader, JsonSeri
/// <param name="writer">The writer to write JSON to</param>
/// <param name="value">The date interval to serialize</param>
/// <param name="options">The serializer options for embedded serialization.</param>
/// <param name="isProperty">Conditional to indicate which function to invoke on the writer</param>
protected override void WriteJsonImpl(Utf8JsonWriter writer, DateInterval value, JsonSerializerOptions options, bool isProperty = false)
protected override void WriteJsonImpl(Utf8JsonWriter writer, DateInterval value, JsonSerializerOptions options)
{
var pattern = LocalDatePattern.Iso;
var text = $"{pattern.Format(value.Start)}/{pattern.Format(value.End)}";
if (isProperty)
writer.WritePropertyName(text);
else
writer.WriteStringValue(text);
writer.WriteStringValue(text);
}

protected override void WriteJsonPropertyNameImpl(Utf8JsonWriter writer, DateInterval value, JsonSerializerOptions options)
{
var pattern = LocalDatePattern.Iso;
var text = $"{pattern.Format(value.Start)}/{pattern.Format(value.End)}";
writer.WritePropertyName(text);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,18 @@ protected override Interval ReadJsonImpl(ref Utf8JsonReader reader, JsonSerializ
/// <param name="writer">The writer to write JSON to</param>
/// <param name="value">The interval to serialize</param>
/// <param name="options">The serializer options for embedded serialization.</param>
/// <param name="isProperty">Interval cannot be converted to a scalar string value as such it is not a valid choice for a dictionary key</param>
protected override void WriteJsonImpl(Utf8JsonWriter writer, Interval value, JsonSerializerOptions options, bool isProperty = false)
protected override void WriteJsonImpl(Utf8JsonWriter writer, Interval value, JsonSerializerOptions options)
{
var pattern = InstantPattern.ExtendedIso;
var text = $"{(value.HasStart ? pattern.Format(value.Start) : "")}/{(value.HasEnd ? pattern.Format(value.End) : "")}";
if (isProperty)
writer.WritePropertyName(text);
else
writer.WriteStringValue(text);
writer.WriteStringValue(text);
}

protected override void WriteJsonPropertyNameImpl(Utf8JsonWriter writer, Interval value, JsonSerializerOptions options)
{
var pattern = InstantPattern.ExtendedIso;
var text = $"{(value.HasStart ? pattern.Format(value.Start) : "")}/{(value.HasEnd ? pattern.Format(value.End) : "")}";
writer.WritePropertyName(text);
}
}
}
23 changes: 16 additions & 7 deletions src/NodaTime.Serialization.SystemTextJson/NodaPatternConverter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
// Use of this source code is governed by the Apache License 2.0,
// as found in the LICENSE.txt file.

using NodaTime.Text;
using System;
using System.Text.Json;
using NodaTime.Text;

namespace NodaTime.Serialization.SystemTextJson
{
Expand Down Expand Up @@ -61,15 +61,24 @@ protected override T ReadJsonImpl(ref Utf8JsonReader reader, JsonSerializerOptio
/// <param name="writer">The writer to write JSON data to</param>
/// <param name="value">The value to serialize</param>
/// <param name="options">The serializer options to use for nested serialization</param>
/// <param name="isProperty">Conditional to indicate which function to invoke on the writer</param>
protected override void WriteJsonImpl(Utf8JsonWriter writer, T value, JsonSerializerOptions options, bool isProperty = false)
protected override void WriteJsonImpl(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
validator?.Invoke(value);
var text = pattern.Format(value);
writer.WriteStringValue(text);
}

/// <summary>
/// Writes the formatted value to the writer.
/// </summary>
/// <param name="writer">The writer to write JSON data to.</param>
/// <param name="value">The value to serialize.</param>
/// <param name="options">The serialization options to use for nested serialization.</param>
protected override void WriteJsonPropertyNameImpl(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
validator?.Invoke(value);
var text = pattern.Format(value);
if (isProperty)
writer.WritePropertyName(text);
else
writer.WriteStringValue(text);
writer.WritePropertyName(text);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,11 @@ protected override int ReadJsonImpl(ref Utf8JsonReader reader, JsonSerializerOpt
return int.Parse(reader.GetString());
}

protected override void WriteJsonImpl(Utf8JsonWriter writer, int value, JsonSerializerOptions options, bool isProperty = false)
{
protected override void WriteJsonImpl(Utf8JsonWriter writer, int value, JsonSerializerOptions options) =>
writer.WriteStringValue(value.ToString());
}

protected override void WriteJsonPropertyNameImpl(Utf8JsonWriter writer, int value, JsonSerializerOptions options) =>
writer.WritePropertyName(value.ToString());
}

private class TestStringConverter : NodaConverterBase<string>
Expand All @@ -88,10 +89,11 @@ protected override string ReadJsonImpl(ref Utf8JsonReader reader, JsonSerializer
return reader.GetString();
}

protected override void WriteJsonImpl(Utf8JsonWriter writer, string value, JsonSerializerOptions options, bool isProperty = false)
{
protected override void WriteJsonImpl(Utf8JsonWriter writer, string value, JsonSerializerOptions options) =>
writer.WriteStringValue(value);
}

protected override void WriteJsonPropertyNameImpl(Utf8JsonWriter writer, string value, JsonSerializerOptions options) =>
writer.WritePropertyName(value);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

using NodaTime.Serialization.SystemTextJson;
using NUnit.Framework;
using System.Collections.Generic;
using System.Text.Json;
using static NodaTime.Serialization.Test.SystemText.TestHelper;

Expand Down Expand Up @@ -165,6 +166,16 @@ public void Deserialize_CaseInsensitive_CamelCase()
Assert.AreEqual(expectedInterval, intervalCamelCase);
}

[Test]
public void CannotUseDateIntervalAsPropertyName()
{
var obj = new Dictionary<DateInterval, string>
{
{ new DateInterval(new LocalDate(2012, 1, 2), new LocalDate(2013, 6, 7)), "Test" }
};
Assert.Throws<JsonException>(() => JsonSerializer.Serialize(obj, options));
}

public class TestObject
{
public DateInterval Interval { get; set; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Use of this source code is governed by the Apache License 2.0,
// as found in the LICENSE.txt file.

using System.Collections.Generic;
using System.Text.Json;
using NodaTime.Serialization.SystemTextJson;
using NUnit.Framework;
Expand Down Expand Up @@ -171,6 +172,16 @@ public void Deserialize_CaseInsensitive_CamelCase()
Assert.AreEqual(expectedInterval, intervalCamelCase);
}

[Test]
public void CannotUseIntervalAsPropertyName()
{
var obj = new Dictionary<Interval, string>
{
{ new Interval(NodaConstants.UnixEpoch, NodaConstants.UnixEpoch), "Test" }
};
Assert.Throws<JsonException>(() => JsonSerializer.Serialize(obj, options));
}

public class TestObject
{
public Interval Interval { get; set; }
Expand Down

0 comments on commit 080b304

Please sign in to comment.