v14: Refactor and enhance System.Text.Json converters (#15960)
* Update JsonUdiConverter to support Udi, GuidUdi and StringUdi types * Require boolean (like) value and rename to JsonFuzzyBooleanConverter * Add read/write only JsonConverters and align naming * Rename SystemTextJsonSerializer to DefaultJsonSerializer * Rename SystemTextConfigurationEditorJsonSerializer to DefaultConfigurationEditorJsonSerializer * Add JsonUdiRangeConverter * Rename JsonFuzzyBooleanConverter back to JsonBooleanConverter * Fix value type check in JsonObjectConverter * Revert class names * Updated tests * Post fix after merge. --------- Co-authored-by: Bjarke Berg <mail@bergmania.dk>
This commit is contained in:
@@ -1,26 +0,0 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
public class AutoInterningStringConverter : JsonConverter<string>
|
||||
{
|
||||
// This is a hacky workaround to creating a "read only converter", since System.Text.Json doesn't support it.
|
||||
// Taken from https://github.com/dotnet/runtime/issues/46372#issuecomment-1660515178
|
||||
private readonly JsonConverter<string> _fallbackConverter = (JsonConverter<string>)JsonSerializerOptions.Default.GetConverter(typeof(string));
|
||||
|
||||
public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
|
||||
reader.TokenType switch
|
||||
{
|
||||
JsonTokenType.Null => null,
|
||||
JsonTokenType.String =>
|
||||
// It's safe to ignore nullability here, because according to the docs:
|
||||
// Returns null when TokenType is JsonTokenType.Null
|
||||
// https://learn.microsoft.com/en-us/dotnet/api/system.text.json.utf8jsonreader.getstring?view=net-8.0#remarks
|
||||
string.Intern(reader.GetString()!),
|
||||
_ => throw new InvalidOperationException($"{nameof(AutoInterningStringConverter)} only supports strings."),
|
||||
};
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, string value, JsonSerializerOptions options)
|
||||
=> _fallbackConverter.Write(writer, value, options);
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
public class AutoInterningStringKeyCaseInsensitiveDictionaryConverter<TValue> : JsonConverter<IDictionary<string, TValue>>
|
||||
{
|
||||
// This is a hacky workaround to creating a "read only converter", since System.Text.Json doesn't support it.
|
||||
// Taken from https://github.com/dotnet/runtime/issues/46372#issuecomment-1660515178
|
||||
private readonly JsonConverter<IDictionary<string, TValue>> _fallbackConverter = (JsonConverter<IDictionary<string, TValue>>)JsonSerializerOptions.Default.GetConverter(typeof(IDictionary<string, TValue>));
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvert(Type typeToConvert) => typeof(IDictionary<string, TValue>).IsAssignableFrom(typeToConvert);
|
||||
|
||||
public override Dictionary<string, TValue>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType != JsonTokenType.StartObject)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
var dictionary = new Dictionary<string, TValue>(StringComparer.OrdinalIgnoreCase);
|
||||
while (reader.Read())
|
||||
{
|
||||
switch (reader.TokenType)
|
||||
{
|
||||
case JsonTokenType.PropertyName:
|
||||
var key = string.Intern(reader.GetString()!);
|
||||
|
||||
if (reader.Read() is false)
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
|
||||
TValue? value = JsonSerializer.Deserialize<TValue>(ref reader, options);
|
||||
dictionary[key] = value!;
|
||||
break;
|
||||
case JsonTokenType.Comment:
|
||||
break;
|
||||
case JsonTokenType.EndObject:
|
||||
return dictionary;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, IDictionary<string, TValue> value, JsonSerializerOptions options) => _fallbackConverter.Write(writer, value, options);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
public class CaseInsensitiveDictionaryConverter<TValue> : JsonConverter<IDictionary<string, TValue>>
|
||||
{
|
||||
// This is a hacky workaround to creating a "read only converter", since System.Text.Json doesn't support it.
|
||||
// Taken from https://github.com/dotnet/runtime/issues/46372#issuecomment-1660515178
|
||||
private readonly JsonConverter<IDictionary<string, TValue>> _fallbackConverter = (JsonConverter<IDictionary<string, TValue>>)JsonSerializerOptions.Default.GetConverter(typeof(IDictionary<string, TValue>));
|
||||
|
||||
public override IDictionary<string, TValue>? Read(
|
||||
ref Utf8JsonReader reader,
|
||||
Type typeToConvert,
|
||||
JsonSerializerOptions options)
|
||||
{
|
||||
IDictionary<string, TValue>? defaultDictionary = JsonSerializer.Deserialize<IDictionary<string, TValue>>(ref reader, options);
|
||||
return defaultDictionary is null
|
||||
? null
|
||||
: new Dictionary<string, TValue>(defaultDictionary, StringComparer.OrdinalIgnoreCase);
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, IDictionary<string, TValue> value, JsonSerializerOptions options) => _fallbackConverter.Write(writer, value, options);
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// In order to match the existing behaviour, and that os messagepack, we need to ensure that DateTimes are always read as UTC.
|
||||
/// This is not the case by default for System.Text.Json, see: https://github.com/dotnet/runtime/issues/1566
|
||||
/// </summary>
|
||||
public class ForceUtcDateTimeConverter : JsonConverter<DateTime>
|
||||
{
|
||||
private readonly JsonConverter<DateTime> _fallBackConverter = (JsonConverter<DateTime>)JsonSerializerOptions.Default.GetConverter(typeof(DateTime));
|
||||
|
||||
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
=> reader.GetDateTime().ToUniversalTime();
|
||||
|
||||
// The existing behaviour is fine for writing, it's only reading that's an issue.
|
||||
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
|
||||
=> _fallBackConverter.Write(writer, value, options);
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
public class JsonBoolConverter : JsonConverter<bool>
|
||||
{
|
||||
public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options) =>
|
||||
writer.WriteBooleanValue(value);
|
||||
|
||||
public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) =>
|
||||
reader.TokenType switch
|
||||
{
|
||||
JsonTokenType.True => true,
|
||||
JsonTokenType.False => false,
|
||||
JsonTokenType.String => ParseString(ref reader),
|
||||
JsonTokenType.Number => reader.TryGetInt64(out long l) ? Convert.ToBoolean(l) : reader.TryGetDouble(out double d) ? Convert.ToBoolean(d) : false,
|
||||
_ => throw new JsonException(),
|
||||
};
|
||||
|
||||
private bool ParseString(ref Utf8JsonReader reader)
|
||||
{
|
||||
var value = reader.GetString();
|
||||
if (bool.TryParse(value, out var b))
|
||||
{
|
||||
return b;
|
||||
}
|
||||
|
||||
if (int.TryParse(value, out var i))
|
||||
{
|
||||
return Convert.ToBoolean(i);
|
||||
}
|
||||
|
||||
throw new JsonException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a boolean value to or from JSON, always converting a boolean like value (like <c>1</c> or <c>0</c>) to a boolean.
|
||||
/// </summary>
|
||||
public sealed class JsonBooleanConverter : JsonConverter<bool>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
=> reader.TokenType switch
|
||||
{
|
||||
JsonTokenType.String => ParseString(ref reader),
|
||||
JsonTokenType.Number => ParseNumber(ref reader),
|
||||
JsonTokenType.True => true,
|
||||
JsonTokenType.False => false,
|
||||
_ => throw new JsonException(),
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options)
|
||||
=> writer.WriteBooleanValue(value);
|
||||
|
||||
private static bool ParseString(ref Utf8JsonReader reader)
|
||||
{
|
||||
var value = reader.GetString();
|
||||
|
||||
if (bool.TryParse(value, out var boolValue))
|
||||
{
|
||||
return boolValue;
|
||||
}
|
||||
|
||||
if (long.TryParse(value, out var longValue))
|
||||
{
|
||||
return Convert.ToBoolean(longValue);
|
||||
}
|
||||
|
||||
if (double.TryParse(value, out double doubleValue))
|
||||
{
|
||||
return Convert.ToBoolean(doubleValue);
|
||||
}
|
||||
|
||||
throw new JsonException();
|
||||
}
|
||||
|
||||
private static bool ParseNumber(ref Utf8JsonReader reader)
|
||||
{
|
||||
if (reader.TryGetInt64(out long longValue))
|
||||
{
|
||||
return Convert.ToBoolean(longValue);
|
||||
}
|
||||
|
||||
if (reader.TryGetDouble(out double doubleValue))
|
||||
{
|
||||
return Convert.ToBoolean(doubleValue);
|
||||
}
|
||||
|
||||
throw new JsonException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a dictionary with a string key to or from JSON, using the <see cref="StringComparer.OrdinalIgnoreCase" /> comparer.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">The type of the dictionary value.</typeparam>
|
||||
public sealed class JsonDictionaryStringIgnoreCaseConverter<TValue> : ReadOnlyJsonConverter<Dictionary<string, TValue>>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override Dictionary<string, TValue>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType != JsonTokenType.StartObject)
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
|
||||
var dictionary = new Dictionary<string, TValue>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.EndObject)
|
||||
{
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
// Get key
|
||||
if (reader.TokenType != JsonTokenType.PropertyName)
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
|
||||
string propertyName = reader.GetString() ?? throw new JsonException();
|
||||
|
||||
// Get value
|
||||
reader.Read();
|
||||
TValue? value = JsonSerializer.Deserialize<TValue>(ref reader, options);
|
||||
if (value is not null)
|
||||
{
|
||||
dictionary[propertyName] = value;
|
||||
}
|
||||
}
|
||||
|
||||
throw new JsonException();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a dictionary with a string key to or from JSON, using the <see cref="StringComparer.OrdinalIgnoreCase" /> comparer and interning the string key when reading.
|
||||
/// </summary>
|
||||
/// <typeparam name="TValue">The type of the dictionary value.</typeparam>
|
||||
public sealed class JsonDictionaryStringInternIgnoreCaseConverter<TValue> : ReadOnlyJsonConverter<Dictionary<string, TValue>>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override Dictionary<string, TValue>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
if (reader.TokenType != JsonTokenType.StartObject)
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
|
||||
var dictionary = new Dictionary<string, TValue>(StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
while (reader.Read())
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.EndObject)
|
||||
{
|
||||
return dictionary;
|
||||
}
|
||||
|
||||
// Get key
|
||||
if (reader.TokenType != JsonTokenType.PropertyName)
|
||||
{
|
||||
throw new JsonException();
|
||||
}
|
||||
|
||||
string propertyName = reader.GetString() ?? throw new JsonException();
|
||||
|
||||
// Get value
|
||||
reader.Read();
|
||||
TValue? value = JsonSerializer.Deserialize<TValue>(ref reader, options);
|
||||
if (value is not null)
|
||||
{
|
||||
dictionary[string.Intern(propertyName)] = value;
|
||||
}
|
||||
}
|
||||
|
||||
throw new JsonException();
|
||||
}
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
public class JsonGuidUdiConverter : JsonConverter<GuidUdi>
|
||||
{
|
||||
public override GuidUdi? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var stringValue = reader.GetString();
|
||||
return stringValue.IsNullOrWhiteSpace() == false && UdiParser.TryParse(stringValue, out Udi? udi) && udi is GuidUdi guidUdi
|
||||
? guidUdi
|
||||
: null;
|
||||
}
|
||||
|
||||
public override void Write(Utf8JsonWriter writer, GuidUdi value, JsonSerializerOptions options)
|
||||
=> writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
@@ -1,43 +1,43 @@
|
||||
using System.Collections;
|
||||
using System.Collections;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Nodes;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
public class JsonObjectConverter : JsonConverter<object>
|
||||
/// <summary>
|
||||
/// Converts an object to or from JSON.
|
||||
/// </summary>
|
||||
public sealed class JsonObjectConverter : JsonConverter<object>
|
||||
{
|
||||
public override object? Read(
|
||||
ref Utf8JsonReader reader,
|
||||
Type typeToConvert,
|
||||
JsonSerializerOptions options) =>
|
||||
ParseObject(ref reader);
|
||||
/// <inheritdoc />
|
||||
public override object? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
=> ParseObject(ref reader);
|
||||
|
||||
public override void Write(
|
||||
Utf8JsonWriter writer,
|
||||
object objectToWrite,
|
||||
JsonSerializerOptions options)
|
||||
/// <inheritdoc />
|
||||
public override void Write(Utf8JsonWriter writer, object value, JsonSerializerOptions options)
|
||||
{
|
||||
if (objectToWrite is null)
|
||||
if (value is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// If an object is equals "new object()", Json.Serialize would recurse forever and cause a stack overflow
|
||||
// If the value equals an empty object, Json.Serialize would recurse forever and cause a stack overflow
|
||||
// We have no good way of checking if its an empty object
|
||||
// which is why we try to check if the object has any properties, and thus will be empty.
|
||||
if (objectToWrite.GetType().Name is "Object" && !objectToWrite.GetType().GetProperties().Any())
|
||||
Type inputType = value.GetType();
|
||||
if (inputType == typeof(object) && inputType.GetProperties().Length == 0)
|
||||
{
|
||||
writer.WriteStartObject();
|
||||
writer.WriteEndObject();
|
||||
}
|
||||
else
|
||||
{
|
||||
JsonSerializer.Serialize(writer, objectToWrite, objectToWrite.GetType(), options);
|
||||
JsonSerializer.Serialize(writer, value, inputType, options);
|
||||
}
|
||||
}
|
||||
|
||||
private object? ParseObject(ref Utf8JsonReader reader)
|
||||
private static object? ParseObject(ref Utf8JsonReader reader)
|
||||
{
|
||||
if (reader.TokenType == JsonTokenType.StartArray)
|
||||
{
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a string to or from JSON, interning the string when reading.
|
||||
/// </summary>
|
||||
public sealed class JsonStringInternConverter : ReadOnlyJsonConverter<string>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override string? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
=> reader.GetString() is string value
|
||||
? string.Intern(value)
|
||||
: null;
|
||||
}
|
||||
@@ -1,20 +1,25 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Umbraco.Cms.Core;
|
||||
using Umbraco.Extensions;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
public class JsonUdiConverter : JsonConverter<Udi>
|
||||
/// <summary>
|
||||
/// Converts an <see cref="Udi" /> to or from JSON.
|
||||
/// </summary>
|
||||
public sealed class JsonUdiConverter : JsonConverter<Udi>
|
||||
{
|
||||
public override Udi? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
{
|
||||
var stringValue = reader.GetString();
|
||||
return stringValue.IsNullOrWhiteSpace() == false && UdiParser.TryParse(stringValue, out Udi? udi)
|
||||
? udi
|
||||
: null;
|
||||
}
|
||||
/// <inheritdoc />
|
||||
public override bool CanConvert(Type typeToConvert)
|
||||
=> typeof(Udi).IsAssignableFrom(typeToConvert);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Udi? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
=> reader.GetString() is string value
|
||||
? UdiParser.Parse(value)
|
||||
: null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write(Utf8JsonWriter writer, Udi value, JsonSerializerOptions options)
|
||||
=> writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Umbraco.Cms.Core;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Converts an <see cref="UdiRange" /> to or from JSON.
|
||||
/// </summary>
|
||||
public sealed class JsonUdiRangeConverter : JsonConverter<UdiRange>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override UdiRange? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
=> reader.GetString() is string value
|
||||
? UdiRange.Parse(value)
|
||||
: null;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Write(Utf8JsonWriter writer, UdiRange value, JsonSerializerOptions options)
|
||||
=> writer.WriteStringValue(value.ToString());
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Converts a DateTime value to or from JSON, always converting the value to Coordinated Universal Time (UTC) when reading.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// In order to match the existing behaviour, and that of MessagePack, we need to ensure that DateTimes are always read as UTC.
|
||||
/// This is not the case by default for System.Text.Json, see: https://github.com/dotnet/runtime/issues/1566.
|
||||
/// </remarks>
|
||||
public sealed class JsonUniversalDateTimeConverter : ReadOnlyJsonConverter<DateTime>
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
=> reader.GetDateTime().ToUniversalTime();
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Converts an object or value from JSON.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object or value handled by the converter.</typeparam>
|
||||
public abstract class ReadOnlyJsonConverter<T> : JsonConverter<T>
|
||||
{
|
||||
private readonly JsonConverter<T> _fallbackConverter = (JsonConverter<T>)JsonSerializerOptions.Default.GetConverter(typeof(T));
|
||||
|
||||
/// <inheritdoc />
|
||||
public sealed override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
|
||||
=> _fallbackConverter.Write(writer, value, options);
|
||||
}
|
||||
@@ -1,36 +1,38 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
// FIXME: clean up all config editor serializers when we can migrate fully to System.Text.Json
|
||||
// - move this implementation to ConfigurationEditorJsonSerializer (delete the old implementation)
|
||||
// - use this implementation as the registered singleton (delete ContextualConfigurationEditorJsonSerializer)
|
||||
// - reuse the JsonObjectConverter implementation from management API (delete the local implementation - pending V12 branch update)
|
||||
|
||||
public class SystemTextConfigurationEditorJsonSerializer : IConfigurationEditorJsonSerializer
|
||||
/// <inheritdoc />
|
||||
public sealed class SystemTextConfigurationEditorJsonSerializer : IConfigurationEditorJsonSerializer
|
||||
{
|
||||
private JsonSerializerOptions _jsonSerializerOptions;
|
||||
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SystemTextConfigurationEditorJsonSerializer" /> class.
|
||||
/// </summary>
|
||||
public SystemTextConfigurationEditorJsonSerializer()
|
||||
{
|
||||
_jsonSerializerOptions = new JsonSerializerOptions
|
||||
=> _jsonSerializerOptions = new JsonSerializerOptions()
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
// in some cases, configs aren't camel cased in the DB, so we have to resort to case insensitive
|
||||
// property name resolving when creating configuration objects (deserializing DB configs)
|
||||
PropertyNameCaseInsensitive = true,
|
||||
NumberHandling = JsonNumberHandling.AllowReadingFromString
|
||||
NumberHandling = JsonNumberHandling.AllowReadingFromString,
|
||||
Converters =
|
||||
{
|
||||
new JsonStringEnumConverter(),
|
||||
new JsonObjectConverter(),
|
||||
new JsonUdiConverter(),
|
||||
new JsonUdiRangeConverter(),
|
||||
new JsonBooleanConverter()
|
||||
}
|
||||
};
|
||||
_jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||
_jsonSerializerOptions.Converters.Add(new JsonObjectConverter());
|
||||
_jsonSerializerOptions.Converters.Add(new JsonUdiConverter());
|
||||
_jsonSerializerOptions.Converters.Add(new JsonGuidUdiConverter());
|
||||
_jsonSerializerOptions.Converters.Add(new JsonBoolConverter());
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Serialize(object? input) => JsonSerializer.Serialize(input, _jsonSerializerOptions);
|
||||
|
||||
/// <inheritdoc />
|
||||
public T? Deserialize<T>(string input) => JsonSerializer.Deserialize<T>(input, _jsonSerializerOptions);
|
||||
}
|
||||
|
||||
@@ -1,24 +1,34 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
using Umbraco.Cms.Core.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
public class SystemTextJsonSerializer : IJsonSerializer
|
||||
/// <inheritdoc />
|
||||
public sealed class SystemTextJsonSerializer : IJsonSerializer
|
||||
{
|
||||
private readonly JsonSerializerOptions _jsonSerializerOptions;
|
||||
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="SystemTextJsonSerializer" /> class.
|
||||
/// </summary>
|
||||
public SystemTextJsonSerializer()
|
||||
{
|
||||
_jsonSerializerOptions = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase };
|
||||
_jsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||
_jsonSerializerOptions.Converters.Add(new JsonUdiConverter());
|
||||
_jsonSerializerOptions.Converters.Add(new JsonGuidUdiConverter());
|
||||
// we may need to add JsonObjectConverter at some point, but for the time being things work fine without
|
||||
// _jsonSerializerOptions.Converters.Add(new JsonObjectConverter());
|
||||
}
|
||||
=> _jsonSerializerOptions = new JsonSerializerOptions
|
||||
{
|
||||
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
|
||||
Converters =
|
||||
{
|
||||
new JsonStringEnumConverter(),
|
||||
new JsonUdiConverter(),
|
||||
new JsonUdiRangeConverter(),
|
||||
// We may need to add JsonObjectConverter at some point, but for the time being things work fine without
|
||||
//new JsonObjectConverter()
|
||||
}
|
||||
};
|
||||
|
||||
/// <inheritdoc />
|
||||
public string Serialize(object? input) => JsonSerializer.Serialize(input, _jsonSerializerOptions);
|
||||
|
||||
/// <inheritdoc />
|
||||
public T? Deserialize<T>(string input) => JsonSerializer.Deserialize<T>(input, _jsonSerializerOptions);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Umbraco.Cms.Infrastructure.Serialization;
|
||||
|
||||
/// <summary>
|
||||
/// Converts an object or value to JSON.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">The type of object or value handled by the converter.</typeparam>
|
||||
public abstract class WriteOnlyJsonConverter<T> : JsonConverter<T>
|
||||
{
|
||||
private readonly JsonConverter<T> _fallbackConverter = (JsonConverter<T>)JsonSerializerOptions.Default.GetConverter(typeof(T));
|
||||
|
||||
/// <inheritdoc />
|
||||
public sealed override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
||||
=> _fallbackConverter.Read(ref reader, typeToConvert, options);
|
||||
}
|
||||
Reference in New Issue
Block a user