Clean up json Converters

This commit is contained in:
crobibero 2020-08-26 08:22:48 -06:00
parent 582c016d3b
commit 8b96881aa1
12 changed files with 11 additions and 437 deletions

View File

@ -169,6 +169,7 @@ namespace Jellyfin.Server.Extensions
options.JsonSerializerOptions.ReadCommentHandling = jsonOptions.ReadCommentHandling;
options.JsonSerializerOptions.WriteIndented = jsonOptions.WriteIndented;
options.JsonSerializerOptions.DefaultIgnoreCondition = jsonOptions.DefaultIgnoreCondition;
options.JsonSerializerOptions.NumberHandling = jsonOptions.NumberHandling;
options.JsonSerializerOptions.Converters.Clear();
foreach (var converter in jsonOptions.Converters)

View File

@ -1,56 +0,0 @@
using System;
using System.Buffers;
using System.Buffers.Text;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace MediaBrowser.Common.Json.Converters
{
/// <summary>
/// Double to String JSON converter.
/// Web client send quoted doubles.
/// </summary>
public class JsonDoubleConverter : JsonConverter<double>
{
/// <summary>
/// Read JSON string as double.
/// </summary>
/// <param name="reader"><see cref="Utf8JsonReader"/>.</param>
/// <param name="typeToConvert">Type.</param>
/// <param name="options">Options.</param>
/// <returns>Parsed value.</returns>
public override double Read(ref Utf8JsonReader reader, Type typeToConvert, 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 double number, out var bytesConsumed) && span.Length == bytesConsumed)
{
return number;
}
// try to parse from a string if the above failed, this covers cases with other escaped/UTF characters
if (double.TryParse(reader.GetString(), out number))
{
return number;
}
}
// fallback to default handling
return reader.GetDouble();
}
/// <summary>
/// Write double to JSON string.
/// </summary>
/// <param name="writer"><see cref="Utf8JsonWriter"/>.</param>
/// <param name="value">Value to write.</param>
/// <param name="options">Options.</param>
public override void Write(Utf8JsonWriter writer, double value, JsonSerializerOptions options)
{
writer.WriteNumberValue(value);
}
}
}

View File

@ -1,40 +0,0 @@
using System;
using System.Buffers;
using System.Buffers.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace MediaBrowser.Common.Json.Converters
{
/// <summary>
/// Converts a int32 object or value to/from JSON.
/// </summary>
public class JsonInt32Converter : JsonConverter<int>
{
/// <inheritdoc />
public override int Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.String)
{
ReadOnlySpan<byte> span = reader.HasValueSequence ? reader.ValueSequence.ToArray() : reader.ValueSpan;
if (Utf8Parser.TryParse(span, out int number, out int bytesConsumed) && span.Length == bytesConsumed)
{
return number;
}
if (int.TryParse(reader.GetString(), out number))
{
return number;
}
}
return reader.GetInt32();
}
/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, int value, JsonSerializerOptions options)
{
writer.WriteNumberValue(value);
}
}
}

View File

@ -1,56 +0,0 @@
using System;
using System.Buffers;
using System.Buffers.Text;
using System.Globalization;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace MediaBrowser.Common.Json.Converters
{
/// <summary>
/// Parse JSON string as long.
/// Javascript does not support 64-bit integers.
/// </summary>
public class JsonInt64Converter : JsonConverter<long>
{
/// <summary>
/// Read JSON string as int64.
/// </summary>
/// <param name="reader"><see cref="Utf8JsonReader"/>.</param>
/// <param name="type">Type.</param>
/// <param name="options">Options.</param>
/// <returns>Parsed value.</returns>
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;
}
// try to parse from a string if the above failed, this covers cases with other escaped/UTF characters
if (long.TryParse(reader.GetString(), out number))
{
return number;
}
}
// fallback to default handling
return reader.GetInt64();
}
/// <summary>
/// Write long to JSON long.
/// </summary>
/// <param name="writer"><see cref="Utf8JsonWriter"/>.</param>
/// <param name="value">Value to write.</param>
/// <param name="options">Options.</param>
public override void Write(Utf8JsonWriter writer, long value, JsonSerializerOptions options)
{
writer.WriteNumberValue(value);
}
}
}

View File

@ -1,82 +0,0 @@
#nullable enable
using System;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace MediaBrowser.Common.Json.Converters
{
/// <summary>
/// Converter for Dictionaries without string key.
/// TODO This can be removed when System.Text.Json supports Dictionaries with non-string keys.
/// </summary>
/// <typeparam name="TKey">Type of key.</typeparam>
/// <typeparam name="TValue">Type of value.</typeparam>
internal sealed class JsonNonStringKeyDictionaryConverter<TKey, TValue> : JsonConverter<IDictionary<TKey, TValue>>
{
/// <summary>
/// Read JSON.
/// </summary>
/// <param name="reader">The Utf8JsonReader.</param>
/// <param name="typeToConvert">The type to convert.</param>
/// <param name="options">The json serializer options.</param>
/// <returns>Typed dictionary.</returns>
/// <exception cref="NotSupportedException">Dictionary key type not supported.</exception>
public override IDictionary<TKey, TValue> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
var convertedType = typeof(Dictionary<,>).MakeGenericType(typeof(string), typeToConvert.GenericTypeArguments[1]);
var value = JsonSerializer.Deserialize(ref reader, convertedType, options);
var instance = (Dictionary<TKey, TValue>)Activator.CreateInstance(
typeToConvert,
BindingFlags.Instance | BindingFlags.Public,
null,
null,
CultureInfo.CurrentCulture);
var enumerator = (IEnumerator)convertedType.GetMethod("GetEnumerator")!.Invoke(value, null);
var parse = typeof(TKey).GetMethod(
"Parse",
0,
BindingFlags.Public | BindingFlags.Static,
null,
CallingConventions.Any,
new[] { typeof(string) },
null);
if (parse == null)
{
throw new NotSupportedException($"{typeof(TKey)} as TKey in IDictionary<TKey, TValue> is not supported.");
}
while (enumerator.MoveNext())
{
var element = (KeyValuePair<string?, TValue>)enumerator.Current;
instance.Add((TKey)parse.Invoke(null, new[] { (object?)element.Key }), element.Value);
}
return instance;
}
/// <summary>
/// Write dictionary as Json.
/// </summary>
/// <param name="writer">The Utf8JsonWriter.</param>
/// <param name="value">The dictionary value.</param>
/// <param name="options">The Json serializer options.</param>
public override void Write(Utf8JsonWriter writer, IDictionary<TKey, TValue> value, JsonSerializerOptions options)
{
var convertedDictionary = new Dictionary<string?, TValue>(value.Count);
foreach (var (k, v) in value)
{
if (k != null)
{
convertedDictionary[k.ToString()] = v;
}
}
JsonSerializer.Serialize(writer, convertedDictionary, options);
}
}
}

View File

@ -1,59 +0,0 @@
#nullable enable
using System;
using System.Collections;
using System.Globalization;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace MediaBrowser.Common.Json.Converters
{
/// <summary>
/// https://github.com/dotnet/runtime/issues/30524#issuecomment-524619972.
/// TODO This can be removed when System.Text.Json supports Dictionaries with non-string keys.
/// </summary>
internal sealed class JsonNonStringKeyDictionaryConverterFactory : JsonConverterFactory
{
/// <summary>
/// Only convert objects that implement IDictionary and do not have string keys.
/// </summary>
/// <param name="typeToConvert">Type convert.</param>
/// <returns>Conversion ability.</returns>
public override bool CanConvert(Type typeToConvert)
{
if (!typeToConvert.IsGenericType)
{
return false;
}
// Let built in converter handle string keys
if (typeToConvert.GenericTypeArguments[0] == typeof(string))
{
return false;
}
// Only support objects that implement IDictionary
return typeToConvert.GetInterface(nameof(IDictionary)) != null;
}
/// <summary>
/// Create converter for generic dictionary type.
/// </summary>
/// <param name="typeToConvert">Type to convert.</param>
/// <param name="options">Json serializer options.</param>
/// <returns>JsonConverter for given type.</returns>
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
{
var converterType = typeof(JsonNonStringKeyDictionaryConverter<,>)
.MakeGenericType(typeToConvert.GenericTypeArguments[0], typeToConvert.GenericTypeArguments[1]);
var converter = (JsonConverter)Activator.CreateInstance(
converterType,
BindingFlags.Instance | BindingFlags.Public,
null,
null,
CultureInfo.CurrentCulture);
return converter;
}
}
}

View File

@ -1,60 +0,0 @@
using System;
using System.Buffers;
using System.Buffers.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace MediaBrowser.Common.Json.Converters
{
/// <summary>
/// Converts a nullable int32 object or value to/from JSON.
/// </summary>
public class JsonNullableInt32Converter : JsonConverter<int?>
{
/// <inheritdoc />
public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
{
return null;
}
if (reader.TokenType == JsonTokenType.String)
{
ReadOnlySpan<byte> 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();
}
/// <inheritdoc />
public override void Write(Utf8JsonWriter writer, int? value, JsonSerializerOptions options)
{
if (value is null)
{
writer.WriteNullValue();
}
else
{
writer.WriteNumberValue(value.Value);
}
}
}
}

View File

@ -1,75 +0,0 @@
using System;
using System.Buffers;
using System.Buffers.Text;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace MediaBrowser.Common.Json.Converters
{
/// <summary>
/// Parse JSON string as nullable long.
/// Javascript does not support 64-bit integers.
/// </summary>
public class JsonNullableInt64Converter : JsonConverter<long?>
{
/// <summary>
/// Read JSON string as int64.
/// </summary>
/// <param name="reader"><see cref="Utf8JsonReader"/>.</param>
/// <param name="type">Type.</param>
/// <param name="options">Options.</param>
/// <returns>Parsed value.</returns>
public override long? Read(ref Utf8JsonReader reader, Type type, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
{
return null;
}
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();
}
/// <summary>
/// Write long to JSON long.
/// </summary>
/// <param name="writer"><see cref="Utf8JsonWriter"/>.</param>
/// <param name="value">Value to write.</param>
/// <param name="options">Options.</param>
public override void Write(Utf8JsonWriter writer, long? value, JsonSerializerOptions options)
{
if (value is null)
{
writer.WriteNullValue();
}
else
{
writer.WriteNumberValue(value.Value);
}
}
}
}

View File

@ -25,17 +25,12 @@ namespace MediaBrowser.Common.Json
{
ReadCommentHandling = JsonCommentHandling.Disallow,
WriteIndented = false,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
NumberHandling = JsonNumberHandling.AllowReadingFromString
};
options.Converters.Add(new JsonGuidConverter());
options.Converters.Add(new JsonInt32Converter());
options.Converters.Add(new JsonNullableInt32Converter());
options.Converters.Add(new JsonStringEnumConverter());
options.Converters.Add(new JsonNonStringKeyDictionaryConverterFactory());
options.Converters.Add(new JsonInt64Converter());
options.Converters.Add(new JsonNullableInt64Converter());
options.Converters.Add(new JsonDoubleConverter());
return options;
}

View File

@ -12,6 +12,7 @@ using System.Threading;
using System.Threading.Tasks;
using MediaBrowser.Common.Configuration;
using MediaBrowser.Common.Extensions;
using MediaBrowser.Common.Json;
using MediaBrowser.Controller.Configuration;
using MediaBrowser.Controller.MediaEncoding;
using MediaBrowser.MediaEncoding.Probing;
@ -54,6 +55,9 @@ namespace MediaBrowser.MediaEncoding.Encoder
private readonly object _runningProcessesLock = new object();
private readonly List<ProcessWrapper> _runningProcesses = new List<ProcessWrapper>();
// MediaEncoder is registered as a Singleton
private readonly JsonSerializerOptions _jsonSerializerOptions;
private List<string> _encoders = new List<string>();
private List<string> _decoders = new List<string>();
private List<string> _hwaccels = new List<string>();
@ -75,6 +79,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
_localization = localization;
_encodingHelperFactory = encodingHelperFactory;
_startupOptionFFmpegPath = config.GetValue<string>(Controller.Extensions.ConfigurationExtensions.FfmpegPathKey) ?? string.Empty;
_jsonSerializerOptions = JsonDefaults.GetOptions();
}
private EncodingHelper EncodingHelper => _encodingHelperFactory.Value;
@ -414,6 +419,7 @@ namespace MediaBrowser.MediaEncoding.Encoder
{
result = await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(
process.StandardOutput.BaseStream,
_jsonSerializerOptions,
cancellationToken: cancellationToken).ConfigureAwait(false);
}
catch

View File

@ -133,7 +133,6 @@ namespace MediaBrowser.MediaEncoding.Probing
/// </summary>
/// <value>The bits_per_raw_sample.</value>
[JsonPropertyName("bits_per_raw_sample")]
[JsonConverter(typeof(JsonInt32Converter))]
public int BitsPerRawSample { get; set; }
/// <summary>

View File

@ -1,6 +1,7 @@
using System.IO;
using System.Text.Json;
using System.Threading.Tasks;
using MediaBrowser.Common.Json;
using MediaBrowser.MediaEncoding.Probing;
using Xunit;
@ -15,7 +16,7 @@ namespace Jellyfin.MediaEncoding.Tests
var path = Path.Join("Test Data", fileName);
using (var stream = File.OpenRead(path))
{
await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(stream).ConfigureAwait(false);
await JsonSerializer.DeserializeAsync<InternalMediaInfoResult>(stream, JsonDefaults.GetOptions()).ConfigureAwait(false);
}
}
}