New: Use System.Text.Json for Nancy and SignalR

This commit is contained in:
ta264
2021-02-10 21:52:48 +00:00
parent 16b3817202
commit d3e8c7e0c9
40 changed files with 378 additions and 88 deletions
@@ -11,6 +11,7 @@
<PackageReference Include="Sentry" Version="2.1.4" /> <PackageReference Include="Sentry" Version="2.1.4" />
<PackageReference Include="SharpZipLib" Version="1.2.0" /> <PackageReference Include="SharpZipLib" Version="1.2.0" />
<PackageReference Include="System.IO.Abstractions" Version="12.0.4" /> <PackageReference Include="System.IO.Abstractions" Version="12.0.4" />
<PackageReference Include="System.Text.Json" Version="5.0.1" />
<PackageReference Include="System.ValueTuple" Version="4.5.0" /> <PackageReference Include="System.ValueTuple" Version="4.5.0" />
<PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.113.0-0" /> <PackageReference Include="System.Data.SQLite.Core.Servarr" Version="1.0.113.0-0" />
</ItemGroup> </ItemGroup>
@@ -77,10 +77,5 @@ namespace NzbDrone.Common.Serializer
Serializer.Serialize(jsonTextWriter, model); Serializer.Serialize(jsonTextWriter, model);
jsonTextWriter.Flush(); jsonTextWriter.Flush();
} }
public static void Serialize<TModel>(TModel model, Stream outputStream)
{
Serialize(model, new StreamWriter(outputStream));
}
} }
} }
@@ -0,0 +1,19 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace NzbDrone.Common.Serializer
{
public class PolymorphicWriteOnlyJsonConverter<T> : JsonConverter<T>
{
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
throw new NotImplementedException();
}
public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options)
{
JsonSerializer.Serialize(writer, value, value.GetType(), options);
}
}
}
@@ -0,0 +1,27 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
using NzbDrone.Common.Http;
namespace NzbDrone.Common.Serializer
{
public class STJHttpUriConverter : JsonConverter<HttpUri>
{
public override HttpUri Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return new HttpUri(reader.GetString());
}
public override void Write(Utf8JsonWriter writer, HttpUri value, JsonSerializerOptions options)
{
if (value == null)
{
writer.WriteNullValue();
}
else
{
writer.WriteStringValue(value.FullUri);
}
}
}
}
@@ -2,9 +2,9 @@
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
namespace NzbDrone.Core.Datastore.Converters namespace NzbDrone.Common.Serializer
{ {
public class TimeSpanConverter : JsonConverter<TimeSpan> public class STJTimeSpanConverter : JsonConverter<TimeSpan>
{ {
public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{ {
@@ -0,0 +1,19 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace NzbDrone.Common.Serializer
{
public class STJUtcConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.Parse(reader.GetString());
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"));
}
}
}
@@ -0,0 +1,48 @@
using System;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace NzbDrone.Common.Serializer
{
public class STJVersionConverter : JsonConverter<Version>
{
public override Version Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
if (reader.TokenType == JsonTokenType.Null)
{
return null;
}
else
{
if (reader.TokenType == JsonTokenType.String)
{
try
{
Version v = new Version(reader.GetString());
return v;
}
catch (Exception)
{
throw new JsonException();
}
}
else
{
throw new JsonException();
}
}
}
public override void Write(Utf8JsonWriter writer, Version value, JsonSerializerOptions options)
{
if (value == null)
{
writer.WriteNullValue();
}
else
{
writer.WriteStringValue(value.ToString());
}
}
}
}
@@ -0,0 +1,88 @@
using System;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace NzbDrone.Common.Serializer
{
public static class STJson
{
private static readonly JsonSerializerOptions SerializerSettings = GetSerializerSettings();
private static readonly JsonWriterOptions WriterOptions = new JsonWriterOptions
{
Indented = true
};
public static JsonSerializerOptions GetSerializerSettings()
{
var serializerSettings = new JsonSerializerOptions
{
AllowTrailingCommas = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
PropertyNameCaseInsensitive = true,
DictionaryKeyPolicy = JsonNamingPolicy.CamelCase,
PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
WriteIndented = true
};
serializerSettings.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true));
serializerSettings.Converters.Add(new STJVersionConverter());
serializerSettings.Converters.Add(new STJHttpUriConverter());
serializerSettings.Converters.Add(new STJTimeSpanConverter());
serializerSettings.Converters.Add(new STJUtcConverter());
return serializerSettings;
}
public static T Deserialize<T>(string json)
where T : new()
{
return JsonSerializer.Deserialize<T>(json, SerializerSettings);
}
public static object Deserialize(string json, Type type)
{
return JsonSerializer.Deserialize(json, type, SerializerSettings);
}
public static object Deserialize(Stream input, Type type)
{
return JsonSerializer.DeserializeAsync(input, type, SerializerSettings).GetAwaiter().GetResult();
}
public static bool TryDeserialize<T>(string json, out T result)
where T : new()
{
try
{
result = Deserialize<T>(json);
return true;
}
catch (JsonException)
{
result = default(T);
return false;
}
}
public static string ToJson(object obj)
{
return JsonSerializer.Serialize(obj, SerializerSettings);
}
public static void Serialize<TModel>(TModel model, Stream outputStream, JsonSerializerOptions options = null)
{
if (options == null)
{
options = SerializerSettings;
}
// Cast to object to get all properties written out
// https://github.com/dotnet/corefx/issues/38650
using (var writer = new Utf8JsonWriter(outputStream, options: WriterOptions))
{
JsonSerializer.Serialize(writer, (object)model, options);
}
}
}
}
@@ -2,6 +2,7 @@ using System.Data;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Dapper; using Dapper;
using NzbDrone.Common.Serializer;
namespace NzbDrone.Core.Datastore.Converters namespace NzbDrone.Core.Datastore.Converters
{ {
@@ -22,8 +23,8 @@ namespace NzbDrone.Core.Datastore.Converters
}; };
serializerSettings.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true)); serializerSettings.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true));
serializerSettings.Converters.Add(new TimeSpanConverter()); serializerSettings.Converters.Add(new STJTimeSpanConverter());
serializerSettings.Converters.Add(new UtcConverter()); serializerSettings.Converters.Add(new STJUtcConverter());
SerializerSettings = serializerSettings; SerializerSettings = serializerSettings;
} }
@@ -1,7 +1,5 @@
using System; using System;
using System.Data; using System.Data;
using System.Text.Json;
using System.Text.Json.Serialization;
using Dapper; using Dapper;
namespace NzbDrone.Core.Datastore.Converters namespace NzbDrone.Core.Datastore.Converters
@@ -18,17 +16,4 @@ namespace NzbDrone.Core.Datastore.Converters
return (DateTime)value; return (DateTime)value;
} }
} }
public class UtcConverter : JsonConverter<DateTime>
{
public override DateTime Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
return DateTime.Parse(reader.GetString());
}
public override void Write(Utf8JsonWriter writer, DateTime value, JsonSerializerOptions options)
{
writer.WriteStringValue(value.ToUniversalTime().ToString("yyyy'-'MM'-'dd'T'HH':'mm':'ssZ"));
}
}
} }
+2 -5
View File
@@ -1,4 +1,5 @@
using System; using System;
using System.Text.Json.Serialization;
using NLog; using NLog;
using NzbDrone.Common.Instrumentation; using NzbDrone.Common.Instrumentation;
@@ -15,6 +16,7 @@ namespace NzbDrone.Core.Datastore
/// Allows a field to be lazy loaded. /// Allows a field to be lazy loaded.
/// </summary> /// </summary>
/// <typeparam name="TChild"></typeparam> /// <typeparam name="TChild"></typeparam>
[JsonConverter(typeof(LazyLoadedConverterFactory))]
public class LazyLoaded<TChild> : ILazyLoaded public class LazyLoaded<TChild> : ILazyLoaded
{ {
protected TChild _value; protected TChild _value;
@@ -62,11 +64,6 @@ namespace NzbDrone.Core.Datastore
{ {
return MemberwiseClone(); return MemberwiseClone();
} }
public bool ShouldSerializeValue()
{
return IsLoaded;
}
} }
/// <summary> /// <summary>
@@ -0,0 +1,90 @@
using System;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
namespace NzbDrone.Core.Datastore
{
public class LazyLoadedConverterFactory : JsonConverterFactory
{
public override bool CanConvert(Type typeToConvert)
{
if (!typeToConvert.IsGenericType)
{
return false;
}
return typeToConvert.GetGenericTypeDefinition() == typeof(LazyLoaded<>);
}
public override JsonConverter CreateConverter(Type type, JsonSerializerOptions options)
{
var childType = type.GetGenericArguments()[0];
return (JsonConverter)Activator.CreateInstance(
typeof(LazyLoadedConverter<>).MakeGenericType(childType),
BindingFlags.Instance | BindingFlags.Public,
binder: null,
args: new object[] { options },
culture: null);
}
private class LazyLoadedConverter<TChild> : JsonConverter<LazyLoaded<TChild>>
{
private readonly JsonConverter<TChild> _childConverter;
private readonly Type _childType;
public LazyLoadedConverter(JsonSerializerOptions options)
{
// For performance, use the existing converter if available.
_childConverter = (JsonConverter<TChild>)options
.GetConverter(typeof(TChild));
// Cache the type.
_childType = typeof(TChild);
}
public override LazyLoaded<TChild> Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
{
TChild value;
if (_childConverter != null)
{
reader.Read();
value = _childConverter.Read(ref reader, _childType, options);
}
else
{
value = JsonSerializer.Deserialize<TChild>(ref reader, options);
}
if (value != null)
{
return new LazyLoaded<TChild>(value);
}
else
{
return null;
}
}
public override void Write(Utf8JsonWriter writer, LazyLoaded<TChild> value, JsonSerializerOptions options)
{
if (value.IsLoaded)
{
if (_childConverter != null)
{
_childConverter.Write(writer, value.Value, options);
}
else
{
JsonSerializer.Serialize(writer, value.Value, options);
}
}
else
{
writer.WriteNullValue();
}
}
}
}
}
@@ -1,7 +1,10 @@
using System; using System;
using System.Text.Json.Serialization;
using NzbDrone.Common.Serializer;
namespace NzbDrone.Core.Messaging.Commands namespace NzbDrone.Core.Messaging.Commands
{ {
[JsonConverter(typeof(PolymorphicWriteOnlyJsonConverter<Command>))]
public abstract class Command public abstract class Command
{ {
private bool _sendUpdatesToClient; private bool _sendUpdatesToClient;
@@ -1,6 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using System.Text.Json.Serialization;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Qualities; using NzbDrone.Core.Qualities;
@@ -9,7 +9,7 @@ namespace NzbDrone.Core.Profiles.Qualities
{ {
public class QualityProfileQualityItem : IEmbeddedDocument public class QualityProfileQualityItem : IEmbeddedDocument
{ {
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public int Id { get; set; } public int Id { get; set; }
public string Name { get; set; } public string Name { get; set; }
+1 -1
View File
@@ -1,6 +1,6 @@
using System; using System;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using System.Text.Json.Serialization;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
namespace NzbDrone.Core.Qualities namespace NzbDrone.Core.Qualities
+1 -1
View File
@@ -14,7 +14,7 @@
<PackageReference Include="FluentValidation" Version="8.6.2" /> <PackageReference Include="FluentValidation" Version="8.6.2" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="NLog" Version="4.7.2" /> <PackageReference Include="NLog" Version="4.7.2" />
<PackageReference Include="RestSharp" Version="106.11.4" /> <PackageReference Include="RestSharp" Version="106.11.7" />
<PackageReference Include="System.IO.Abstractions" Version="12.0.4" /> <PackageReference Include="System.IO.Abstractions" Version="12.0.4" />
<PackageReference Include="TagLibSharp-Lidarr" Version="2.2.0.19" /> <PackageReference Include="TagLibSharp-Lidarr" Version="2.2.0.19" />
<PackageReference Include="Kveer.XmlRPC" Version="1.1.1" /> <PackageReference Include="Kveer.XmlRPC" Version="1.1.1" />
@@ -1,11 +1,11 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Text.Json; using System.Text.Json;
using System.Text.Json.Serialization; using System.Text.Json.Serialization;
using Dapper; using Dapper;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Common.Reflection; using NzbDrone.Common.Reflection;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using NzbDrone.Core.Datastore.Converters;
using NzbDrone.Core.Messaging.Events; using NzbDrone.Core.Messaging.Events;
namespace NzbDrone.Core.ThingiProvider namespace NzbDrone.Core.ThingiProvider
@@ -30,8 +30,8 @@ namespace NzbDrone.Core.ThingiProvider
}; };
serializerSettings.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true)); serializerSettings.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true));
serializerSettings.Converters.Add(new TimeSpanConverter()); serializerSettings.Converters.Add(new STJTimeSpanConverter());
serializerSettings.Converters.Add(new UtcConverter()); serializerSettings.Converters.Add(new STJUtcConverter());
_serializerSettings = serializerSettings; _serializerSettings = serializerSettings;
} }
-1
View File
@@ -11,7 +11,6 @@
</ItemGroup> </ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net5.0'"> <ItemGroup Condition="'$(TargetFramework)' == 'net5.0'">
<PackageReference Include="Microsoft.AspNetCore.Owin" Version="5.0.3" /> <PackageReference Include="Microsoft.AspNetCore.Owin" Version="5.0.3" />
<PackageReference Include="Microsoft.AspNetCore.SignalR.Protocols.NewtonsoftJson" Version="5.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="NLog.Extensions.Logging" Version="1.6.4" /> <PackageReference Include="NLog.Extensions.Logging" Version="1.6.4" />
+8 -10
View File
@@ -91,17 +91,15 @@ namespace NzbDrone.Host
services services
.AddSignalR() .AddSignalR()
#if !NETCOREAPP #if !NETCOREAPP
.AddJsonProtocol( .AddJsonProtocol(options =>
options => {
{ options.PayloadSerializerSettings = Json.GetSerializerSettings();
options.PayloadSerializerSettings = Json.GetSerializerSettings(); });
});
#else #else
.AddNewtonsoftJsonProtocol( .AddJsonProtocol(options =>
options => {
{ options.PayloadSerializerOptions = STJson.GetSerializerSettings();
options.PayloadSerializerSettings = Json.GetSerializerSettings(); });
});
#endif #endif
}) })
.Configure(app => .Configure(app =>
@@ -51,7 +51,7 @@ namespace NzbDrone.Integration.Test.Client
throw response.ErrorException; throw response.ErrorException;
} }
AssertDisableCache(response.Headers); AssertDisableCache(response);
response.ErrorMessage.Should().BeNullOrWhiteSpace(); response.ErrorMessage.Should().BeNullOrWhiteSpace();
@@ -68,9 +68,10 @@ namespace NzbDrone.Integration.Test.Client
return Json.Deserialize<T>(content); return Json.Deserialize<T>(content);
} }
private static void AssertDisableCache(IList<Parameter> headers) private static void AssertDisableCache(IRestResponse response)
{ {
// cache control header gets reordered on net core // cache control header gets reordered on net core
var headers = response.Headers;
((string)headers.Single(c => c.Name == "Cache-Control").Value).Split(',').Select(x => x.Trim()) ((string)headers.Single(c => c.Name == "Cache-Control").Value).Split(',').Select(x => x.Trim())
.Should().BeEquivalentTo("no-store, must-revalidate, no-cache, max-age=0".Split(',').Select(x => x.Trim())); .Should().BeEquivalentTo("no-store, must-revalidate, no-cache, max-age=0".Split(',').Select(x => x.Trim()));
headers.Single(c => c.Name == "Pragma").Value.Should().Be("no-cache"); headers.Single(c => c.Name == "Pragma").Value.Should().Be("no-cache");
@@ -28,6 +28,7 @@ using Readarr.Api.V1.Profiles.Quality;
using Readarr.Api.V1.RootFolders; using Readarr.Api.V1.RootFolders;
using Readarr.Api.V1.Tags; using Readarr.Api.V1.Tags;
using RestSharp; using RestSharp;
using RestSharp.Serializers.SystemTextJson;
namespace NzbDrone.Integration.Test namespace NzbDrone.Integration.Test
{ {
@@ -98,6 +99,7 @@ namespace NzbDrone.Integration.Test
RestClient = new RestClient(RootUrl + "api/v1/"); RestClient = new RestClient(RootUrl + "api/v1/");
RestClient.AddDefaultHeader("Authentication", ApiKey); RestClient.AddDefaultHeader("Authentication", ApiKey);
RestClient.AddDefaultHeader("X-Api-Key", ApiKey); RestClient.AddDefaultHeader("X-Api-Key", ApiKey);
RestClient.UseSystemTextJson();
Blacklist = new ClientBase<BlacklistResource>(RestClient, ApiKey); Blacklist = new ClientBase<BlacklistResource>(RestClient, ApiKey);
Commands = new CommandClient(RestClient, ApiKey); Commands = new CommandClient(RestClient, ApiKey);
+5 -2
View File
@@ -1,4 +1,3 @@
using Newtonsoft.Json;
using NzbDrone.Core.Datastore.Events; using NzbDrone.Core.Datastore.Events;
namespace NzbDrone.SignalR namespace NzbDrone.SignalR
@@ -8,7 +7,11 @@ namespace NzbDrone.SignalR
public object Body { get; set; } public object Body { get; set; }
public string Name { get; set; } public string Name { get; set; }
[JsonIgnore] #if !NETCOREAPP
[Newtonsoft.Json.JsonIgnore]
#else
[System.Text.Json.Serialization.JsonIgnore]
#endif
public ModelAction Action { get; set; } public ModelAction Action { get; set; }
} }
} }
@@ -9,7 +9,8 @@
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="NLog" Version="4.7.2" /> <PackageReference Include="NLog" Version="4.7.2" />
<PackageReference Include="NUnit" Version="3.12.0" /> <PackageReference Include="NUnit" Version="3.12.0" />
<PackageReference Include="RestSharp" Version="106.11.4" /> <PackageReference Include="RestSharp" Version="106.11.7" />
<PackageReference Include="RestSharp.Serializers.SystemTextJson" Version="106.11.7" />
<PackageReference Include="System.IO.Abstractions" Version="12.0.4" /> <PackageReference Include="System.IO.Abstractions" Version="12.0.4" />
<PackageReference Include="Unity" Version="5.11.7" /> <PackageReference Include="Unity" Version="5.11.7" />
</ItemGroup> </ItemGroup>
+3 -2
View File
@@ -16,6 +16,7 @@ using NzbDrone.Core.RootFolders;
using NzbDrone.Core.Validation; using NzbDrone.Core.Validation;
using NzbDrone.Core.Validation.Paths; using NzbDrone.Core.Validation.Paths;
using NzbDrone.SignalR; using NzbDrone.SignalR;
using Readarr.Api.V1.Books;
using Readarr.Http; using Readarr.Http;
using Readarr.Http.Extensions; using Readarr.Http.Extensions;
@@ -189,8 +190,8 @@ namespace Readarr.Api.V1.Author
foreach (var authorResource in authors) foreach (var authorResource in authors)
{ {
authorResource.NextBook = nextBooks.FirstOrDefault(x => x.AuthorMetadataId == authorResource.AuthorMetadataId); authorResource.NextBook = nextBooks.FirstOrDefault(x => x.AuthorMetadataId == authorResource.AuthorMetadataId).ToResource();
authorResource.LastBook = lastBooks.FirstOrDefault(x => x.AuthorMetadataId == authorResource.AuthorMetadataId); authorResource.LastBook = lastBooks.FirstOrDefault(x => x.AuthorMetadataId == authorResource.AuthorMetadataId).ToResource();
} }
} }
+3 -2
View File
@@ -5,6 +5,7 @@ using Newtonsoft.Json;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Books; using NzbDrone.Core.Books;
using NzbDrone.Core.MediaCover; using NzbDrone.Core.MediaCover;
using Readarr.Api.V1.Books;
using Readarr.Http.REST; using Readarr.Http.REST;
namespace Readarr.Api.V1.Author namespace Readarr.Api.V1.Author
@@ -27,8 +28,8 @@ namespace Readarr.Api.V1.Author
public string Disambiguation { get; set; } public string Disambiguation { get; set; }
public List<Links> Links { get; set; } public List<Links> Links { get; set; }
public Book NextBook { get; set; } public BookResource NextBook { get; set; }
public Book LastBook { get; set; } public BookResource LastBook { get; set; }
public List<MediaCover> Images { get; set; } public List<MediaCover> Images { get; set; }
@@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using System.Text.Json.Serialization;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Core.Messaging.Commands; using NzbDrone.Core.Messaging.Commands;
using Readarr.Http.REST; using Readarr.Http.REST;
@@ -1,4 +1,5 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Dynamic;
using System.Linq; using System.Linq;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
using NzbDrone.Core.CustomFilters; using NzbDrone.Core.CustomFilters;
@@ -10,7 +11,7 @@ namespace Readarr.Api.V1.CustomFilters
{ {
public string Type { get; set; } public string Type { get; set; }
public string Label { get; set; } public string Label { get; set; }
public List<dynamic> Filters { get; set; } public List<ExpandoObject> Filters { get; set; }
} }
public static class CustomFilterResourceMapper public static class CustomFilterResourceMapper
@@ -27,7 +28,7 @@ namespace Readarr.Api.V1.CustomFilters
Id = model.Id, Id = model.Id,
Type = model.Type, Type = model.Type,
Label = model.Label, Label = model.Label,
Filters = Json.Deserialize<List<dynamic>>(model.Filters) Filters = STJson.Deserialize<List<ExpandoObject>>(model.Filters)
}; };
} }
@@ -43,7 +44,7 @@ namespace Readarr.Api.V1.CustomFilters
Id = resource.Id, Id = resource.Id,
Type = resource.Type, Type = resource.Type,
Label = resource.Label, Label = resource.Label,
Filters = Json.ToJson(resource.Filters) Filters = STJson.ToJson(resource.Filters)
}; };
} }
@@ -1,7 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Newtonsoft.Json; using System.Text.Json.Serialization;
using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.DecisionEngine;
using NzbDrone.Core.Indexers; using NzbDrone.Core.Indexers;
using NzbDrone.Core.Parser.Model; using NzbDrone.Core.Parser.Model;
@@ -49,14 +49,10 @@ namespace Readarr.Api.V1.Indexers
public DownloadProtocol Protocol { get; set; } public DownloadProtocol Protocol { get; set; }
// Sent when queuing an unknown release // Sent when queuing an unknown release
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
// [JsonIgnore]
public int? AuthorId { get; set; } public int? AuthorId { get; set; }
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
// [JsonIgnore]
public int? BookId { get; set; } public int? BookId { get; set; }
} }
-1
View File
@@ -14,7 +14,6 @@
<PackageReference Include="Nancy" Version="2.0.0" /> <PackageReference Include="Nancy" Version="2.0.0" />
<PackageReference Include="Nancy.Authentication.Basic" Version="2.0.0" /> <PackageReference Include="Nancy.Authentication.Basic" Version="2.0.0" />
<PackageReference Include="Nancy.Authentication.Forms" Version="2.0.0" /> <PackageReference Include="Nancy.Authentication.Forms" Version="2.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="NLog" Version="4.7.2" /> <PackageReference Include="NLog" Version="4.7.2" />
<PackageReference Include="System.IO.Abstractions" Version="12.0.4" /> <PackageReference Include="System.IO.Abstractions" Version="12.0.4" />
</ItemGroup> </ItemGroup>
@@ -1,7 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Newtonsoft.Json;
using NzbDrone.Core.Update; using NzbDrone.Core.Update;
using Readarr.Http.REST; using Readarr.Http.REST;
@@ -9,7 +8,6 @@ namespace Readarr.Api.V1.Update
{ {
public class UpdateResource : RestResource public class UpdateResource : RestResource
{ {
[JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))]
public Version Version { get; set; } public Version Version { get; set; }
public string Branch { get; set; } public string Branch { get; set; }
+18 -6
View File
@@ -2,10 +2,11 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Newtonsoft.Json.Linq; using System.Text.Json;
using NzbDrone.Common.EnsureThat; using NzbDrone.Common.EnsureThat;
using NzbDrone.Common.Extensions; using NzbDrone.Common.Extensions;
using NzbDrone.Common.Reflection; using NzbDrone.Common.Reflection;
using NzbDrone.Common.Serializer;
using NzbDrone.Core.Annotations; using NzbDrone.Core.Annotations;
namespace Readarr.Http.ClientSchema namespace Readarr.Http.ClientSchema
@@ -213,9 +214,9 @@ namespace Readarr.Http.ClientSchema
{ {
return Enumerable.Empty<int>(); return Enumerable.Empty<int>();
} }
else if (fieldValue.GetType() == typeof(JArray)) else if (fieldValue is JsonElement e && e.ValueKind == JsonValueKind.Array)
{ {
return ((JArray)fieldValue).Select(s => s.Value<int>()); return e.EnumerateArray().Select(s => s.GetInt32());
} }
else else
{ {
@@ -231,9 +232,9 @@ namespace Readarr.Http.ClientSchema
{ {
return Enumerable.Empty<string>(); return Enumerable.Empty<string>();
} }
else if (fieldValue.GetType() == typeof(JArray)) else if (fieldValue is JsonElement e && e.ValueKind == JsonValueKind.Array)
{ {
return ((JArray)fieldValue).Select(s => s.Value<string>()); return e.EnumerateArray().Select(s => s.GetString());
} }
else else
{ {
@@ -243,7 +244,18 @@ namespace Readarr.Http.ClientSchema
} }
else else
{ {
return fieldValue => fieldValue; return fieldValue =>
{
var element = fieldValue as JsonElement?;
if (element == null || !element.HasValue)
{
return null;
}
var json = element.Value.GetRawText();
return STJson.Deserialize(json, propertyType);
};
} }
} }
@@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO; using System.IO;
using System.Text.Json;
using Nancy; using Nancy;
using Nancy.Responses.Negotiation; using Nancy.Responses.Negotiation;
using NzbDrone.Common.Serializer; using NzbDrone.Common.Serializer;
@@ -8,6 +9,13 @@ namespace Readarr.Http.Extensions
{ {
public class NancyJsonSerializer : ISerializer public class NancyJsonSerializer : ISerializer
{ {
protected readonly JsonSerializerOptions _serializerSettings;
public NancyJsonSerializer()
{
_serializerSettings = STJson.GetSerializerSettings();
}
public bool CanSerialize(MediaRange contentType) public bool CanSerialize(MediaRange contentType)
{ {
return contentType == "application/json"; return contentType == "application/json";
@@ -15,7 +23,7 @@ namespace Readarr.Http.Extensions
public void Serialize<TModel>(MediaRange contentType, TModel model, Stream outputStream) public void Serialize<TModel>(MediaRange contentType, TModel model, Stream outputStream)
{ {
Json.Serialize(model, outputStream); STJson.Serialize(model, outputStream, _serializerSettings);
} }
public IEnumerable<string> Extensions { get; private set; } public IEnumerable<string> Extensions { get; private set; }
@@ -27,10 +27,8 @@ namespace Readarr.Http.Extensions
public static object FromJson(this Stream body, Type type) public static object FromJson(this Stream body, Type type)
{ {
var reader = new StreamReader(body, true);
body.Position = 0; body.Position = 0;
var value = reader.ReadToEnd(); return STJson.Deserialize(body, type);
return Json.Deserialize(value, type);
} }
public static JsonResponse<TModel> AsResponse<TModel>(this TModel model, NancyContext context, HttpStatusCode statusCode = HttpStatusCode.OK) public static JsonResponse<TModel> AsResponse<TModel>(this TModel model, NancyContext context, HttpStatusCode statusCode = HttpStatusCode.OK)
+3 -3
View File
@@ -1,11 +1,11 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text.Json;
using FluentValidation; using FluentValidation;
using FluentValidation.Results; using FluentValidation.Results;
using Nancy; using Nancy;
using Nancy.Responses.Negotiation; using Nancy.Responses.Negotiation;
using Newtonsoft.Json;
using NzbDrone.Core.Datastore; using NzbDrone.Core.Datastore;
using Readarr.Http.Extensions; using Readarr.Http.Extensions;
@@ -248,9 +248,9 @@ namespace Readarr.Http.REST
{ {
resource = Request.Body.FromJson<TResource>(); resource = Request.Body.FromJson<TResource>();
} }
catch (JsonReaderException ex) catch (JsonException e)
{ {
throw new BadRequestException(ex.Message); throw new BadRequestException(e.Message);
} }
if (resource == null) if (resource == null)
+2 -2
View File
@@ -1,10 +1,10 @@
using Newtonsoft.Json; using System.Text.Json.Serialization;
namespace Readarr.Http.REST namespace Readarr.Http.REST
{ {
public abstract class RestResource public abstract class RestResource
{ {
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)]
public int Id { get; set; } public int Id { get; set; }
[JsonIgnore] [JsonIgnore]
-1
View File
@@ -7,7 +7,6 @@
<PackageReference Include="Nancy" Version="2.0.0" /> <PackageReference Include="Nancy" Version="2.0.0" />
<PackageReference Include="Nancy.Authentication.Basic" Version="2.0.0" /> <PackageReference Include="Nancy.Authentication.Basic" Version="2.0.0" />
<PackageReference Include="Nancy.Authentication.Forms" Version="2.0.0" /> <PackageReference Include="Nancy.Authentication.Forms" Version="2.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
<PackageReference Include="NLog" Version="4.7.2" /> <PackageReference Include="NLog" Version="4.7.2" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>