From 9b2359a4534df1b9700bdac8888e720bb0acda1d Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 1 Sep 2020 08:12:36 -0600 Subject: [PATCH 1/5] readd nullable number converters --- .../Converters/JsonNullableInt32Converter.cs | 56 +++++++++++++++ .../Converters/JsonNullableInt64Converter.cs | 71 +++++++++++++++++++ MediaBrowser.Common/Json/JsonDefaults.cs | 2 + 3 files changed, 129 insertions(+) create mode 100644 MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs create mode 100644 MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs b/MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs new file mode 100644 index 0000000000..6c93cd13e7 --- /dev/null +++ b/MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs @@ -0,0 +1,56 @@ +using System; +using System.Buffers; +using System.Buffers.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace MediaBrowser.Common.Json.Converters +{ + /// + /// Converts a nullable int32 object or value to/from JSON. + /// Required - some clients send an empty string. + /// + public class JsonNullableInt32Converter : JsonConverter + { + /// + public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + ReadOnlySpan span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan; + if (Utf8Parser.TryParse(span, out int number, out int bytesConsumed) && span.Length == bytesConsumed) + { + return number; + } + + var stringValue = reader.GetString().AsSpan(); + + // value is null or empty, just return null. + if (stringValue.IsEmpty) + { + return null; + } + + if (int.TryParse(stringValue, out number)) + { + return number; + } + } + + return reader.GetInt32(); + } + + /// + public override void Write(Utf8JsonWriter writer, int? value, JsonSerializerOptions options) + { + if (value is null) + { + writer.WriteNullValue(); + } + else + { + writer.WriteNumberValue(value.Value); + } + } + } +} diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs b/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs new file mode 100644 index 0000000000..369ea610a4 --- /dev/null +++ b/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs @@ -0,0 +1,71 @@ +using System; +using System.Buffers; +using System.Buffers.Text; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace MediaBrowser.Common.Json.Converters +{ + /// + /// Parse JSON string as nullable long. + /// Javascript does not support 64-bit integers. + /// Required - some clients send an empty string. + /// + public class JsonNullableInt64Converter : JsonConverter + { + /// + /// Read JSON string as int64. + /// + /// . + /// Type. + /// Options. + /// Parsed value. + public override long? Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String) + { + // try to parse number directly from bytes + var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan; + if (Utf8Parser.TryParse(span, out long number, out var bytesConsumed) && span.Length == bytesConsumed) + { + return number; + } + + var stringValue = reader.GetString().AsSpan(); + + // value is null or empty, just return null. + if (stringValue.IsEmpty) + { + return null; + } + + // try to parse from a string if the above failed, this covers cases with other escaped/UTF characters + if (long.TryParse(stringValue, out number)) + { + return number; + } + } + + // fallback to default handling + return reader.GetInt64(); + } + + /// + /// Write long to JSON long. + /// + /// . + /// Value to write. + /// Options. + public override void Write(Utf8JsonWriter writer, long? value, JsonSerializerOptions options) + { + if (value is null) + { + writer.WriteNullValue(); + } + else + { + writer.WriteNumberValue(value.Value); + } + } + } +} diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs index 9d30927dbb..3f74c896f7 100644 --- a/MediaBrowser.Common/Json/JsonDefaults.cs +++ b/MediaBrowser.Common/Json/JsonDefaults.cs @@ -31,6 +31,8 @@ namespace MediaBrowser.Common.Json options.Converters.Add(new JsonGuidConverter()); options.Converters.Add(new JsonStringEnumConverter()); + options.Converters.Add(new JsonNullableInt32Converter()); + options.Converters.Add(new JsonNullableInt64Converter()); return options; } From 1f2d73af8e986b945c53a4a8bc1be1124216589e Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 1 Sep 2020 08:52:50 -0600 Subject: [PATCH 2/5] Only handle empty string or null case --- .../Converters/JsonNullableInt32Converter.cs | 28 ++++------------- .../Converters/JsonNullableInt64Converter.cs | 31 ++++--------------- 2 files changed, 12 insertions(+), 47 deletions(-) diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs b/MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs index 6c93cd13e7..cd0017c780 100644 --- a/MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs @@ -1,6 +1,4 @@ using System; -using System.Buffers; -using System.Buffers.Text; using System.Text.Json; using System.Text.Json.Serialization; @@ -15,29 +13,15 @@ namespace MediaBrowser.Common.Json.Converters /// public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - if (reader.TokenType == JsonTokenType.String) + switch (reader.TokenType) { - ReadOnlySpan span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan; - if (Utf8Parser.TryParse(span, out int number, out int bytesConsumed) && span.Length == bytesConsumed) - { - return number; - } - - var stringValue = reader.GetString().AsSpan(); - - // value is null or empty, just return null. - if (stringValue.IsEmpty) - { + case JsonTokenType.String when (reader.HasValueSequence && reader.ValueSequence.IsEmpty) || reader.ValueSpan.IsEmpty: + case JsonTokenType.Null: return null; - } - - if (int.TryParse(stringValue, out number)) - { - return number; - } + default: + // fallback to default handling + return reader.GetInt32(); } - - return reader.GetInt32(); } /// diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs b/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs index 369ea610a4..8c6879ac7a 100644 --- a/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs @@ -1,6 +1,4 @@ using System; -using System.Buffers; -using System.Buffers.Text; using System.Text.Json; using System.Text.Json.Serialization; @@ -22,32 +20,15 @@ namespace MediaBrowser.Common.Json.Converters /// Parsed value. public override long? Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options) { - if (reader.TokenType == JsonTokenType.String) + switch (reader.TokenType) { - // try to parse number directly from bytes - var span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan; - if (Utf8Parser.TryParse(span, out long number, out var bytesConsumed) && span.Length == bytesConsumed) - { - return number; - } - - var stringValue = reader.GetString().AsSpan(); - - // value is null or empty, just return null. - if (stringValue.IsEmpty) - { + case JsonTokenType.String when (reader.HasValueSequence && reader.ValueSequence.IsEmpty) || reader.ValueSpan.IsEmpty: + case JsonTokenType.Null: return null; - } - - // try to parse from a string if the above failed, this covers cases with other escaped/UTF characters - if (long.TryParse(stringValue, out number)) - { - return number; - } + default: + // fallback to default handling + return reader.GetInt64(); } - - // fallback to default handling - return reader.GetInt64(); } /// From eb400f72928c9a87362c7d763be4627a7f9cd5cb Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 1 Sep 2020 09:19:22 -0600 Subject: [PATCH 3/5] Fallback to base jsonconverter --- .../Converters/JsonNullableInt32Converter.cs | 31 +++++----- .../Converters/JsonNullableInt64Converter.cs | 56 ++++++++----------- MediaBrowser.Common/Json/JsonDefaults.cs | 8 ++- 3 files changed, 45 insertions(+), 50 deletions(-) diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs b/MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs index cd0017c780..a4ecc542ea 100644 --- a/MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs @@ -10,31 +10,32 @@ namespace MediaBrowser.Common.Json.Converters /// public class JsonNullableInt32Converter : JsonConverter { + private readonly JsonConverter _baseJsonConverter; + + /// + /// Initializes a new instance of the class. + /// + /// The base json converter. + public JsonNullableInt32Converter(JsonConverter baseJsonConverter) + { + _baseJsonConverter = baseJsonConverter; + } + /// public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { - switch (reader.TokenType) + if (reader.TokenType == JsonTokenType.String && ((reader.HasValueSequence && reader.ValueSequence.IsEmpty) || reader.ValueSpan.IsEmpty)) { - case JsonTokenType.String when (reader.HasValueSequence && reader.ValueSequence.IsEmpty) || reader.ValueSpan.IsEmpty: - case JsonTokenType.Null: - return null; - default: - // fallback to default handling - return reader.GetInt32(); + return null; } + + return _baseJsonConverter.Read(ref reader, typeToConvert, options); } /// public override void Write(Utf8JsonWriter writer, int? value, JsonSerializerOptions options) { - if (value is null) - { - writer.WriteNullValue(); - } - else - { - writer.WriteNumberValue(value.Value); - } + _baseJsonConverter.Write(writer, value, options); } } } diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs b/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs index 8c6879ac7a..1745a0b09a 100644 --- a/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs @@ -1,52 +1,42 @@ using System; +using System.ComponentModel; using System.Text.Json; using System.Text.Json.Serialization; namespace MediaBrowser.Common.Json.Converters { /// - /// Parse JSON string as nullable long. - /// Javascript does not support 64-bit integers. + /// Converts a nullable int64 object or value to/from JSON. /// Required - some clients send an empty string. /// public class JsonNullableInt64Converter : JsonConverter { - /// - /// Read JSON string as int64. - /// - /// . - /// Type. - /// Options. - /// Parsed value. - public override long? Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options) - { - switch (reader.TokenType) - { - case JsonTokenType.String when (reader.HasValueSequence && reader.ValueSequence.IsEmpty) || reader.ValueSpan.IsEmpty: - case JsonTokenType.Null: - return null; - default: - // fallback to default handling - return reader.GetInt64(); - } - } + private readonly JsonConverter _baseJsonConverter; /// - /// Write long to JSON long. + /// Initializes a new instance of the class. /// - /// . - /// Value to write. - /// Options. + /// The base json converter. + public JsonNullableInt64Converter(JsonConverter baseJsonConverter) + { + _baseJsonConverter = baseJsonConverter; + } + + /// + public override long? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.String && ((reader.HasValueSequence && reader.ValueSequence.IsEmpty) || reader.ValueSpan.IsEmpty)) + { + return null; + } + + return _baseJsonConverter.Read(ref reader, typeToConvert, options); + } + + /// public override void Write(Utf8JsonWriter writer, long? value, JsonSerializerOptions options) { - if (value is null) - { - writer.WriteNullValue(); - } - else - { - writer.WriteNumberValue(value.Value); - } + _baseJsonConverter.Write(writer, value, options); } } } diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs index 3f74c896f7..bbdd1029a9 100644 --- a/MediaBrowser.Common/Json/JsonDefaults.cs +++ b/MediaBrowser.Common/Json/JsonDefaults.cs @@ -29,10 +29,14 @@ namespace MediaBrowser.Common.Json NumberHandling = JsonNumberHandling.AllowReadingFromString }; + // Get built-in converters for fallback converting. + var baseNullableInt32Converter = (JsonConverter)options.GetConverter(typeof(int?)); + var baseNullableInt64Converter = (JsonConverter)options.GetConverter(typeof(long?)); + options.Converters.Add(new JsonGuidConverter()); options.Converters.Add(new JsonStringEnumConverter()); - options.Converters.Add(new JsonNullableInt32Converter()); - options.Converters.Add(new JsonNullableInt64Converter()); + options.Converters.Add(new JsonNullableInt32Converter(baseNullableInt32Converter)); + options.Converters.Add(new JsonNullableInt64Converter(baseNullableInt64Converter)); return options; } From 471e7600572356b51c6e1b49f97410c5e4363a2e Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 1 Sep 2020 09:20:32 -0600 Subject: [PATCH 4/5] remove unused declaration --- .../Json/Converters/JsonNullableInt64Converter.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs b/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs index 1745a0b09a..38e1f8e2f1 100644 --- a/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs @@ -1,5 +1,4 @@ using System; -using System.ComponentModel; using System.Text.Json; using System.Text.Json.Serialization; From 9ddf550b43b3dcaa1129e369242bd664632bff03 Mon Sep 17 00:00:00 2001 From: crobibero Date: Tue, 1 Sep 2020 09:42:59 -0600 Subject: [PATCH 5/5] Simplify json converters --- .../Converters/JsonNullableInt64Converter.cs | 41 ------------------- ...rter.cs => JsonNullableStructConverter.cs} | 17 ++++---- MediaBrowser.Common/Json/JsonDefaults.cs | 4 +- 3 files changed, 12 insertions(+), 50 deletions(-) delete mode 100644 MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs rename MediaBrowser.Common/Json/Converters/{JsonNullableInt32Converter.cs => JsonNullableStructConverter.cs} (59%) diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs b/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs deleted file mode 100644 index 38e1f8e2f1..0000000000 --- a/MediaBrowser.Common/Json/Converters/JsonNullableInt64Converter.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Text.Json; -using System.Text.Json.Serialization; - -namespace MediaBrowser.Common.Json.Converters -{ - /// - /// Converts a nullable int64 object or value to/from JSON. - /// Required - some clients send an empty string. - /// - public class JsonNullableInt64Converter : JsonConverter - { - private readonly JsonConverter _baseJsonConverter; - - /// - /// Initializes a new instance of the class. - /// - /// The base json converter. - public JsonNullableInt64Converter(JsonConverter baseJsonConverter) - { - _baseJsonConverter = baseJsonConverter; - } - - /// - public override long? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - if (reader.TokenType == JsonTokenType.String && ((reader.HasValueSequence && reader.ValueSequence.IsEmpty) || reader.ValueSpan.IsEmpty)) - { - return null; - } - - return _baseJsonConverter.Read(ref reader, typeToConvert, options); - } - - /// - public override void Write(Utf8JsonWriter writer, long? value, JsonSerializerOptions options) - { - _baseJsonConverter.Write(writer, value, options); - } - } -} diff --git a/MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs b/MediaBrowser.Common/Json/Converters/JsonNullableStructConverter.cs similarity index 59% rename from MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs rename to MediaBrowser.Common/Json/Converters/JsonNullableStructConverter.cs index a4ecc542ea..cffc41ba34 100644 --- a/MediaBrowser.Common/Json/Converters/JsonNullableInt32Converter.cs +++ b/MediaBrowser.Common/Json/Converters/JsonNullableStructConverter.cs @@ -5,25 +5,28 @@ using System.Text.Json.Serialization; namespace MediaBrowser.Common.Json.Converters { /// - /// Converts a nullable int32 object or value to/from JSON. + /// Converts a nullable struct or value to/from JSON. /// Required - some clients send an empty string. /// - public class JsonNullableInt32Converter : JsonConverter + /// The struct type. + public class JsonNullableStructConverter : JsonConverter + where T : struct { - private readonly JsonConverter _baseJsonConverter; + private readonly JsonConverter _baseJsonConverter; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The base json converter. - public JsonNullableInt32Converter(JsonConverter baseJsonConverter) + public JsonNullableStructConverter(JsonConverter baseJsonConverter) { _baseJsonConverter = baseJsonConverter; } /// - public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { + // Handle empty string. if (reader.TokenType == JsonTokenType.String && ((reader.HasValueSequence && reader.ValueSequence.IsEmpty) || reader.ValueSpan.IsEmpty)) { return null; @@ -33,7 +36,7 @@ namespace MediaBrowser.Common.Json.Converters } /// - public override void Write(Utf8JsonWriter writer, int? value, JsonSerializerOptions options) + public override void Write(Utf8JsonWriter writer, T? value, JsonSerializerOptions options) { _baseJsonConverter.Write(writer, value, options); } diff --git a/MediaBrowser.Common/Json/JsonDefaults.cs b/MediaBrowser.Common/Json/JsonDefaults.cs index bbdd1029a9..5867cd4a0c 100644 --- a/MediaBrowser.Common/Json/JsonDefaults.cs +++ b/MediaBrowser.Common/Json/JsonDefaults.cs @@ -35,8 +35,8 @@ namespace MediaBrowser.Common.Json options.Converters.Add(new JsonGuidConverter()); options.Converters.Add(new JsonStringEnumConverter()); - options.Converters.Add(new JsonNullableInt32Converter(baseNullableInt32Converter)); - options.Converters.Add(new JsonNullableInt64Converter(baseNullableInt64Converter)); + options.Converters.Add(new JsonNullableStructConverter(baseNullableInt32Converter)); + options.Converters.Add(new JsonNullableStructConverter(baseNullableInt64Converter)); return options; }