diff --git a/src/NzbDrone.Api/Commands/CommandResource.cs b/src/NzbDrone.Api/Commands/CommandResource.cs index 82d97ae7ab..eea9341dc2 100644 --- a/src/NzbDrone.Api/Commands/CommandResource.cs +++ b/src/NzbDrone.Api/Commands/CommandResource.cs @@ -1,7 +1,7 @@ -using System; +using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NzbDrone.Core.Messaging.Commands; using Radarr.Http.REST; diff --git a/src/NzbDrone.Api/Indexers/ReleaseResource.cs b/src/NzbDrone.Api/Indexers/ReleaseResource.cs index c91a629713..0240ef6a58 100644 --- a/src/NzbDrone.Api/Indexers/ReleaseResource.cs +++ b/src/NzbDrone.Api/Indexers/ReleaseResource.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Indexers; using NzbDrone.Core.Languages; diff --git a/src/NzbDrone.Api/Profiles/Languages/LanguageResource.cs b/src/NzbDrone.Api/Profiles/Languages/LanguageResource.cs index 0a4c5cbe7b..ec5b6409d0 100644 --- a/src/NzbDrone.Api/Profiles/Languages/LanguageResource.cs +++ b/src/NzbDrone.Api/Profiles/Languages/LanguageResource.cs @@ -1,11 +1,11 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; using Radarr.Http.REST; namespace NzbDrone.Api.Profiles.Languages { public class LanguageResource : RestResource { - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public new int Id { get; set; } public string Name { get; set; } public string NameLower => Name.ToLowerInvariant(); diff --git a/src/NzbDrone.Api/ProviderModuleBase.cs b/src/NzbDrone.Api/ProviderModuleBase.cs index c57273ff89..81b31c0072 100644 --- a/src/NzbDrone.Api/ProviderModuleBase.cs +++ b/src/NzbDrone.Api/ProviderModuleBase.cs @@ -3,8 +3,8 @@ using FluentValidation; using FluentValidation.Results; using Nancy; -using Newtonsoft.Json; using NzbDrone.Common.Reflection; +using NzbDrone.Common.Serializer; using NzbDrone.Core.ThingiProvider; using NzbDrone.Core.Validation; using Radarr.Http; @@ -188,7 +188,7 @@ private object RequestAction(string action, TProviderResource providerResource) var query = ((IDictionary)Request.Query.ToDictionary()).ToDictionary(k => k.Key, k => k.Value.ToString()); var data = _providerFactory.RequestAction(providerDefinition, action, query); - Response resp = JsonConvert.SerializeObject(data); + Response resp = data.ToJson(); resp.ContentType = "application/json"; return resp; } diff --git a/src/NzbDrone.Api/Radarr.Api.csproj b/src/NzbDrone.Api/Radarr.Api.csproj index 8551fc4610..4c194417cf 100644 --- a/src/NzbDrone.Api/Radarr.Api.csproj +++ b/src/NzbDrone.Api/Radarr.Api.csproj @@ -8,7 +8,6 @@ - diff --git a/src/NzbDrone.Api/Update/UpdateResource.cs b/src/NzbDrone.Api/Update/UpdateResource.cs index dc7f606414..1c46117d55 100644 --- a/src/NzbDrone.Api/Update/UpdateResource.cs +++ b/src/NzbDrone.Api/Update/UpdateResource.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; using NzbDrone.Core.Update; using Radarr.Http.REST; @@ -9,7 +8,6 @@ namespace NzbDrone.Api.Update { public class UpdateResource : RestResource { - [JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))] public Version Version { get; set; } public string Branch { get; set; } diff --git a/src/NzbDrone.Common/Radarr.Common.csproj b/src/NzbDrone.Common/Radarr.Common.csproj index 39dedf7505..f44233c6ca 100644 --- a/src/NzbDrone.Common/Radarr.Common.csproj +++ b/src/NzbDrone.Common/Radarr.Common.csproj @@ -9,6 +9,7 @@ + diff --git a/src/NzbDrone.Common/Serializer/HttpUriConverter.cs b/src/NzbDrone.Common/Serializer/Newtonsoft.Json/HttpUriConverter.cs similarity index 100% rename from src/NzbDrone.Common/Serializer/HttpUriConverter.cs rename to src/NzbDrone.Common/Serializer/Newtonsoft.Json/HttpUriConverter.cs diff --git a/src/NzbDrone.Common/Serializer/IntConverter.cs b/src/NzbDrone.Common/Serializer/Newtonsoft.Json/IntConverter.cs similarity index 100% rename from src/NzbDrone.Common/Serializer/IntConverter.cs rename to src/NzbDrone.Common/Serializer/Newtonsoft.Json/IntConverter.cs diff --git a/src/NzbDrone.Common/Serializer/Json.cs b/src/NzbDrone.Common/Serializer/Newtonsoft.Json/Json.cs similarity index 96% rename from src/NzbDrone.Common/Serializer/Json.cs rename to src/NzbDrone.Common/Serializer/Newtonsoft.Json/Json.cs index 8a23c3f86c..fce2f295f6 100644 --- a/src/NzbDrone.Common/Serializer/Json.cs +++ b/src/NzbDrone.Common/Serializer/Newtonsoft.Json/Json.cs @@ -127,10 +127,5 @@ public static void Serialize(TModel model, TextWriter outputStream) Serializer.Serialize(jsonTextWriter, model); jsonTextWriter.Flush(); } - - public static void Serialize(TModel model, Stream outputStream) - { - Serialize(model, new StreamWriter(outputStream)); - } } } diff --git a/src/NzbDrone.Common/Serializer/JsonVisitor.cs b/src/NzbDrone.Common/Serializer/Newtonsoft.Json/JsonVisitor.cs similarity index 100% rename from src/NzbDrone.Common/Serializer/JsonVisitor.cs rename to src/NzbDrone.Common/Serializer/Newtonsoft.Json/JsonVisitor.cs diff --git a/src/NzbDrone.Common/Serializer/UnderscoreStringEnumConverter.cs b/src/NzbDrone.Common/Serializer/Newtonsoft.Json/UnderscoreStringEnumConverter.cs similarity index 100% rename from src/NzbDrone.Common/Serializer/UnderscoreStringEnumConverter.cs rename to src/NzbDrone.Common/Serializer/Newtonsoft.Json/UnderscoreStringEnumConverter.cs diff --git a/src/NzbDrone.Common/Serializer/System.Text.Json/PolymorphicWriteOnlyJsonConverter.cs b/src/NzbDrone.Common/Serializer/System.Text.Json/PolymorphicWriteOnlyJsonConverter.cs new file mode 100644 index 0000000000..7f4e76c50f --- /dev/null +++ b/src/NzbDrone.Common/Serializer/System.Text.Json/PolymorphicWriteOnlyJsonConverter.cs @@ -0,0 +1,19 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace NzbDrone.Common.Serializer +{ + public class PolymorphicWriteOnlyJsonConverter : JsonConverter + { + 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); + } + } +} diff --git a/src/NzbDrone.Common/Serializer/System.Text.Json/STJHttpUriConverter.cs b/src/NzbDrone.Common/Serializer/System.Text.Json/STJHttpUriConverter.cs new file mode 100644 index 0000000000..cd9f1b46fc --- /dev/null +++ b/src/NzbDrone.Common/Serializer/System.Text.Json/STJHttpUriConverter.cs @@ -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 + { + 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); + } + } + } +} diff --git a/src/NzbDrone.Core/Datastore/Converters/TimeSpanConverter.cs b/src/NzbDrone.Common/Serializer/System.Text.Json/STJTimeSpanConverter.cs similarity index 81% rename from src/NzbDrone.Core/Datastore/Converters/TimeSpanConverter.cs rename to src/NzbDrone.Common/Serializer/System.Text.Json/STJTimeSpanConverter.cs index 07f2d93142..58caacb2b4 100644 --- a/src/NzbDrone.Core/Datastore/Converters/TimeSpanConverter.cs +++ b/src/NzbDrone.Common/Serializer/System.Text.Json/STJTimeSpanConverter.cs @@ -2,9 +2,9 @@ using System.Text.Json; using System.Text.Json.Serialization; -namespace NzbDrone.Core.Datastore.Converters +namespace NzbDrone.Common.Serializer { - public class TimeSpanConverter : JsonConverter + public class STJTimeSpanConverter : JsonConverter { public override TimeSpan Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { diff --git a/src/NzbDrone.Common/Serializer/System.Text.Json/STJUtcConverter.cs b/src/NzbDrone.Common/Serializer/System.Text.Json/STJUtcConverter.cs new file mode 100644 index 0000000000..520b24d6ce --- /dev/null +++ b/src/NzbDrone.Common/Serializer/System.Text.Json/STJUtcConverter.cs @@ -0,0 +1,19 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace NzbDrone.Common.Serializer +{ + public class STJUtcConverter : JsonConverter + { + 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")); + } + } +} diff --git a/src/NzbDrone.Common/Serializer/System.Text.Json/STJVersionConverter.cs b/src/NzbDrone.Common/Serializer/System.Text.Json/STJVersionConverter.cs new file mode 100644 index 0000000000..70ad492c3e --- /dev/null +++ b/src/NzbDrone.Common/Serializer/System.Text.Json/STJVersionConverter.cs @@ -0,0 +1,48 @@ +using System; +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace NzbDrone.Common.Serializer +{ + public class STJVersionConverter : JsonConverter + { + 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()); + } + } + } +} diff --git a/src/NzbDrone.Common/Serializer/System.Text.Json/STJson.cs b/src/NzbDrone.Common/Serializer/System.Text.Json/STJson.cs new file mode 100644 index 0000000000..48b98cf6d2 --- /dev/null +++ b/src/NzbDrone.Common/Serializer/System.Text.Json/STJson.cs @@ -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(string json) + where T : new() + { + return JsonSerializer.Deserialize(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(string json, out T result) + where T : new() + { + try + { + result = Deserialize(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 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); + } + } + } +} diff --git a/src/NzbDrone.Core/Datastore/Converters/EmbeddedDocumentConverter.cs b/src/NzbDrone.Core/Datastore/Converters/EmbeddedDocumentConverter.cs index 6031dcebd8..979d0f60a1 100644 --- a/src/NzbDrone.Core/Datastore/Converters/EmbeddedDocumentConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/EmbeddedDocumentConverter.cs @@ -2,6 +2,7 @@ using System.Text.Json; using System.Text.Json.Serialization; using Dapper; +using NzbDrone.Common.Serializer; namespace NzbDrone.Core.Datastore.Converters { @@ -22,8 +23,8 @@ public EmbeddedDocumentConverter() }; serializerSettings.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true)); - serializerSettings.Converters.Add(new TimeSpanConverter()); - serializerSettings.Converters.Add(new UtcConverter()); + serializerSettings.Converters.Add(new STJTimeSpanConverter()); + serializerSettings.Converters.Add(new STJUtcConverter()); SerializerSettings = serializerSettings; } diff --git a/src/NzbDrone.Core/Datastore/Converters/UtcConverter.cs b/src/NzbDrone.Core/Datastore/Converters/UtcConverter.cs index db70f2359b..cbd379ed2d 100644 --- a/src/NzbDrone.Core/Datastore/Converters/UtcConverter.cs +++ b/src/NzbDrone.Core/Datastore/Converters/UtcConverter.cs @@ -1,7 +1,5 @@ using System; using System.Data; -using System.Text.Json; -using System.Text.Json.Serialization; using Dapper; namespace NzbDrone.Core.Datastore.Converters @@ -18,17 +16,4 @@ public override DateTime Parse(object value) return DateTime.SpecifyKind((DateTime)value, DateTimeKind.Utc); } } - - public class UtcConverter : JsonConverter - { - 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")); - } - } } diff --git a/src/NzbDrone.Core/Messaging/Commands/Command.cs b/src/NzbDrone.Core/Messaging/Commands/Command.cs index 0a41c9078b..aa3ffa6854 100644 --- a/src/NzbDrone.Core/Messaging/Commands/Command.cs +++ b/src/NzbDrone.Core/Messaging/Commands/Command.cs @@ -1,7 +1,10 @@ using System; +using System.Text.Json.Serialization; +using NzbDrone.Common.Serializer; namespace NzbDrone.Core.Messaging.Commands { + [JsonConverter(typeof(PolymorphicWriteOnlyJsonConverter))] public abstract class Command { private bool _sendUpdatesToClient; diff --git a/src/NzbDrone.Core/Profiles/ProfileFormatItem.cs b/src/NzbDrone.Core/Profiles/ProfileFormatItem.cs index f7c8951258..30b3582eee 100644 --- a/src/NzbDrone.Core/Profiles/ProfileFormatItem.cs +++ b/src/NzbDrone.Core/Profiles/ProfileFormatItem.cs @@ -1,4 +1,4 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NzbDrone.Core.CustomFormats; using NzbDrone.Core.Datastore; @@ -6,7 +6,7 @@ namespace NzbDrone.Core.Profiles { public class ProfileFormatItem : IEmbeddedDocument { - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public int Id { get; set; } public CustomFormat Format { get; set; } public int Score { get; set; } diff --git a/src/NzbDrone.Core/Profiles/ProfileQualityItem.cs b/src/NzbDrone.Core/Profiles/ProfileQualityItem.cs index c161a85169..9d5724a437 100644 --- a/src/NzbDrone.Core/Profiles/ProfileQualityItem.cs +++ b/src/NzbDrone.Core/Profiles/ProfileQualityItem.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NzbDrone.Common.Extensions; using NzbDrone.Core.Datastore; using NzbDrone.Core.Qualities; @@ -9,7 +9,7 @@ namespace NzbDrone.Core.Profiles { public class ProfileQualityItem : IEmbeddedDocument { - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public int Id { get; set; } public string Name { get; set; } diff --git a/src/NzbDrone.Core/Qualities/QualityModel.cs b/src/NzbDrone.Core/Qualities/QualityModel.cs index e5d9ef9315..e081b5c3ae 100644 --- a/src/NzbDrone.Core/Qualities/QualityModel.cs +++ b/src/NzbDrone.Core/Qualities/QualityModel.cs @@ -1,5 +1,5 @@ using System; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NzbDrone.Core.Datastore; namespace NzbDrone.Core.Qualities diff --git a/src/NzbDrone.Core/ThingiProvider/ProviderRepository.cs b/src/NzbDrone.Core/ThingiProvider/ProviderRepository.cs index e26b92528b..860ab43d44 100644 --- a/src/NzbDrone.Core/ThingiProvider/ProviderRepository.cs +++ b/src/NzbDrone.Core/ThingiProvider/ProviderRepository.cs @@ -1,11 +1,11 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.Text.Json; using System.Text.Json.Serialization; using Dapper; using NzbDrone.Common.Extensions; using NzbDrone.Common.Reflection; +using NzbDrone.Common.Serializer; using NzbDrone.Core.Datastore; -using NzbDrone.Core.Datastore.Converters; using NzbDrone.Core.Messaging.Events; namespace NzbDrone.Core.ThingiProvider @@ -29,8 +29,8 @@ protected ProviderRepository(IMainDatabase database, IEventAggregator eventAggre }; serializerSettings.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase, true)); - serializerSettings.Converters.Add(new TimeSpanConverter()); - serializerSettings.Converters.Add(new UtcConverter()); + serializerSettings.Converters.Add(new STJTimeSpanConverter()); + serializerSettings.Converters.Add(new STJUtcConverter()); _serializerSettings = serializerSettings; } diff --git a/src/NzbDrone.Host/Radarr.Host.csproj b/src/NzbDrone.Host/Radarr.Host.csproj index 85dc5c3ef7..c5c3f49445 100644 --- a/src/NzbDrone.Host/Radarr.Host.csproj +++ b/src/NzbDrone.Host/Radarr.Host.csproj @@ -11,7 +11,6 @@ - diff --git a/src/NzbDrone.Host/WebHost/WebHostController.cs b/src/NzbDrone.Host/WebHost/WebHostController.cs index 4564f9d70f..440f938cbe 100644 --- a/src/NzbDrone.Host/WebHost/WebHostController.cs +++ b/src/NzbDrone.Host/WebHost/WebHostController.cs @@ -114,9 +114,9 @@ public void StartServer() options.PayloadSerializerSettings = Json.GetSerializerSettings(); }); #else - .AddNewtonsoftJsonProtocol(options => + .AddJsonProtocol(options => { - options.PayloadSerializerSettings = Json.GetSerializerSettings(); + options.PayloadSerializerOptions = STJson.GetSerializerSettings(); }); #endif diff --git a/src/NzbDrone.SignalR/SignalRMessage.cs b/src/NzbDrone.SignalR/SignalRMessage.cs index 548d988a99..81a0a2b2cf 100644 --- a/src/NzbDrone.SignalR/SignalRMessage.cs +++ b/src/NzbDrone.SignalR/SignalRMessage.cs @@ -1,4 +1,3 @@ -using Newtonsoft.Json; using NzbDrone.Core.Datastore.Events; namespace NzbDrone.SignalR @@ -8,7 +7,11 @@ public class SignalRMessage public object Body { 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; } } } diff --git a/src/Radarr.Api.V3/Commands/CommandResource.cs b/src/Radarr.Api.V3/Commands/CommandResource.cs index c3d22c0a4d..b54442995f 100644 --- a/src/Radarr.Api.V3/Commands/CommandResource.cs +++ b/src/Radarr.Api.V3/Commands/CommandResource.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NzbDrone.Common.Extensions; using NzbDrone.Core.Messaging.Commands; using Radarr.Http.REST; diff --git a/src/Radarr.Api.V3/CustomFilters/CustomFilterResource.cs b/src/Radarr.Api.V3/CustomFilters/CustomFilterResource.cs index 358d6865ed..333111bfb5 100644 --- a/src/Radarr.Api.V3/CustomFilters/CustomFilterResource.cs +++ b/src/Radarr.Api.V3/CustomFilters/CustomFilterResource.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.Dynamic; using System.Linq; using NzbDrone.Common.Serializer; using NzbDrone.Core.CustomFilters; @@ -10,7 +11,7 @@ public class CustomFilterResource : RestResource { public string Type { get; set; } public string Label { get; set; } - public List Filters { get; set; } + public List Filters { get; set; } } public static class CustomFilterResourceMapper @@ -27,7 +28,7 @@ public static CustomFilterResource ToResource(this CustomFilter model) Id = model.Id, Type = model.Type, Label = model.Label, - Filters = Json.Deserialize>(model.Filters) + Filters = STJson.Deserialize>(model.Filters) }; } @@ -43,7 +44,7 @@ public static CustomFilter ToModel(this CustomFilterResource resource) Id = resource.Id, Type = resource.Type, Label = resource.Label, - Filters = Json.ToJson(resource.Filters) + Filters = STJson.ToJson(resource.Filters) }; } diff --git a/src/Radarr.Api.V3/CustomFormats/CustomFormatResource.cs b/src/Radarr.Api.V3/CustomFormats/CustomFormatResource.cs index d31e3b0637..2cfcedca28 100644 --- a/src/Radarr.Api.V3/CustomFormats/CustomFormatResource.cs +++ b/src/Radarr.Api.V3/CustomFormats/CustomFormatResource.cs @@ -1,6 +1,6 @@ using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NzbDrone.Core.CustomFormats; using Radarr.Http.ClientSchema; using Radarr.Http.REST; @@ -9,7 +9,7 @@ namespace Radarr.Api.V3.CustomFormats { public class CustomFormatResource : RestResource { - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public override int Id { get; set; } public string Name { get; set; } public bool IncludeCustomFormatWhenRenaming { get; set; } diff --git a/src/Radarr.Api.V3/Indexers/ReleaseResource.cs b/src/Radarr.Api.V3/Indexers/ReleaseResource.cs index b453b10a22..f72a261d78 100644 --- a/src/Radarr.Api.V3/Indexers/ReleaseResource.cs +++ b/src/Radarr.Api.V3/Indexers/ReleaseResource.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; +using System.Text.Json.Serialization; using NzbDrone.Core.DecisionEngine; using NzbDrone.Core.Indexers; using NzbDrone.Core.Languages; @@ -53,7 +53,7 @@ public class ReleaseResource : RestResource public DownloadProtocol Protocol { get; set; } // Sent when queuing an unknown release - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public int? MovieId { get; set; } } diff --git a/src/Radarr.Api.V3/Localization/LocalizationModule.cs b/src/Radarr.Api.V3/Localization/LocalizationModule.cs index 1f02559a4f..2138d5bd2b 100644 --- a/src/Radarr.Api.V3/Localization/LocalizationModule.cs +++ b/src/Radarr.Api.V3/Localization/LocalizationModule.cs @@ -1,4 +1,5 @@ -using Newtonsoft.Json; +using System.Text.Json; +using NzbDrone.Common.Serializer; using NzbDrone.Core.Localization; using Radarr.Http; @@ -7,26 +8,21 @@ namespace Radarr.Api.V3.Localization public class LocalizationModule : RadarrRestModule { private readonly ILocalizationService _localizationService; + private readonly JsonSerializerOptions _serializerSettings; public LocalizationModule(ILocalizationService localizationService) { _localizationService = localizationService; + _serializerSettings = STJson.GetSerializerSettings(); + _serializerSettings.DictionaryKeyPolicy = null; + _serializerSettings.PropertyNamingPolicy = null; Get("/", x => GetLocalizationDictionary()); } private string GetLocalizationDictionary() { - // We don't want camel case for transation strings, create new serializer settings - var serializerSettings = new JsonSerializerSettings - { - DateTimeZoneHandling = DateTimeZoneHandling.Utc, - NullValueHandling = NullValueHandling.Ignore, - Formatting = Formatting.Indented, - DefaultValueHandling = DefaultValueHandling.Include - }; - - return JsonConvert.SerializeObject(_localizationService.GetLocalizationDictionary().ToResource(), serializerSettings); + return JsonSerializer.Serialize(_localizationService.GetLocalizationDictionary().ToResource(), _serializerSettings); } } } diff --git a/src/Radarr.Api.V3/Profiles/Languages/LanguageResource.cs b/src/Radarr.Api.V3/Profiles/Languages/LanguageResource.cs index d0ce8da7c3..64b0390db8 100644 --- a/src/Radarr.Api.V3/Profiles/Languages/LanguageResource.cs +++ b/src/Radarr.Api.V3/Profiles/Languages/LanguageResource.cs @@ -1,11 +1,11 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; using Radarr.Http.REST; namespace Radarr.Api.V3.Profiles.Languages { public class LanguageResource : RestResource { - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Include)] + [JsonIgnore(Condition = JsonIgnoreCondition.Never)] public new int Id { get; set; } public string Name { get; set; } public string NameLower => Name.ToLowerInvariant(); diff --git a/src/Radarr.Api.V3/Radarr.Api.V3.csproj b/src/Radarr.Api.V3/Radarr.Api.V3.csproj index 42669634d2..f711b51f73 100644 --- a/src/Radarr.Api.V3/Radarr.Api.V3.csproj +++ b/src/Radarr.Api.V3/Radarr.Api.V3.csproj @@ -8,7 +8,6 @@ - diff --git a/src/Radarr.Api.V3/Update/UpdateResource.cs b/src/Radarr.Api.V3/Update/UpdateResource.cs index 365619a884..178857a5c6 100644 --- a/src/Radarr.Api.V3/Update/UpdateResource.cs +++ b/src/Radarr.Api.V3/Update/UpdateResource.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using Newtonsoft.Json; using NzbDrone.Core.Update; using Radarr.Http.REST; @@ -9,7 +8,6 @@ namespace Radarr.Api.V3.Update { public class UpdateResource : RestResource { - [JsonConverter(typeof(Newtonsoft.Json.Converters.VersionConverter))] public Version Version { get; set; } public string Branch { get; set; } diff --git a/src/Radarr.Http/ClientSchema/SchemaBuilder.cs b/src/Radarr.Http/ClientSchema/SchemaBuilder.cs index d05855d564..4afd381e1a 100644 --- a/src/Radarr.Http/ClientSchema/SchemaBuilder.cs +++ b/src/Radarr.Http/ClientSchema/SchemaBuilder.cs @@ -2,10 +2,11 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; -using Newtonsoft.Json.Linq; +using System.Text.Json; using NzbDrone.Common.EnsureThat; using NzbDrone.Common.Extensions; using NzbDrone.Common.Reflection; +using NzbDrone.Common.Serializer; using NzbDrone.Core.Annotations; namespace Radarr.Http.ClientSchema @@ -225,9 +226,9 @@ private static Func GetValueConverter(Type propertyType) { return Enumerable.Empty(); } - else if (fieldValue.GetType() == typeof(JArray)) + else if (fieldValue is JsonElement e && e.ValueKind == JsonValueKind.Array) { - return ((JArray)fieldValue).Select(s => s.Value()); + return e.EnumerateArray().Select(s => s.GetInt32()); } else { @@ -243,9 +244,9 @@ private static Func GetValueConverter(Type propertyType) { return Enumerable.Empty(); } - else if (fieldValue.GetType() == typeof(JArray)) + else if (fieldValue is JsonElement e && e.ValueKind == JsonValueKind.Array) { - return ((JArray)fieldValue).Select(s => s.Value()); + return e.EnumerateArray().Select(s => s.GetString()); } else { @@ -255,7 +256,18 @@ private static Func GetValueConverter(Type propertyType) } 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); + }; } } diff --git a/src/Radarr.Http/Extensions/NancyJsonSerializer.cs b/src/Radarr.Http/Extensions/NancyJsonSerializer.cs index 70d91e7ad4..efe6fb2d81 100644 --- a/src/Radarr.Http/Extensions/NancyJsonSerializer.cs +++ b/src/Radarr.Http/Extensions/NancyJsonSerializer.cs @@ -1,5 +1,6 @@ -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; +using System.Text.Json; using Nancy; using Nancy.Responses.Negotiation; using NzbDrone.Common.Serializer; @@ -8,6 +9,13 @@ namespace Radarr.Http.Extensions { public class NancyJsonSerializer : ISerializer { + protected readonly JsonSerializerOptions _serializerSettings; + + public NancyJsonSerializer() + { + _serializerSettings = STJson.GetSerializerSettings(); + } + public bool CanSerialize(MediaRange contentType) { return contentType == "application/json"; @@ -15,7 +23,7 @@ public bool CanSerialize(MediaRange contentType) public void Serialize(MediaRange contentType, TModel model, Stream outputStream) { - Json.Serialize(model, outputStream); + STJson.Serialize(model, outputStream, _serializerSettings); } public IEnumerable Extensions { get; private set; } diff --git a/src/Radarr.Http/Extensions/ReqResExtensions.cs b/src/Radarr.Http/Extensions/ReqResExtensions.cs index d0265a996d..fa032ff5e4 100644 --- a/src/Radarr.Http/Extensions/ReqResExtensions.cs +++ b/src/Radarr.Http/Extensions/ReqResExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using Nancy; @@ -27,10 +27,8 @@ public static T FromJson(this Stream body, Type type) public static object FromJson(this Stream body, Type type) { - var reader = new StreamReader(body, true); body.Position = 0; - var value = reader.ReadToEnd(); - return Json.Deserialize(value, type); + return STJson.Deserialize(body, type); } public static JsonResponse AsResponse(this TModel model, NancyContext context, HttpStatusCode statusCode = HttpStatusCode.OK) diff --git a/src/Radarr.Http/REST/RestModule.cs b/src/Radarr.Http/REST/RestModule.cs index 8473b0a85d..7a09c80f93 100644 --- a/src/Radarr.Http/REST/RestModule.cs +++ b/src/Radarr.Http/REST/RestModule.cs @@ -1,11 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text.Json; using FluentValidation; using FluentValidation.Results; using Nancy; using Nancy.Responses.Negotiation; -using Newtonsoft.Json; using NzbDrone.Core.Datastore; using Radarr.Http.Extensions; @@ -248,7 +248,7 @@ protected TResource ReadResourceFromRequest(bool skipValidate = false, bool skip { resource = Request.Body.FromJson(); } - catch (JsonReaderException e) + catch (JsonException e) { throw new BadRequestException($"Invalid request body. {e.Message}"); } diff --git a/src/Radarr.Http/REST/RestResource.cs b/src/Radarr.Http/REST/RestResource.cs index f3ce82b324..bea9592962 100644 --- a/src/Radarr.Http/REST/RestResource.cs +++ b/src/Radarr.Http/REST/RestResource.cs @@ -1,10 +1,10 @@ -using Newtonsoft.Json; +using System.Text.Json.Serialization; namespace Radarr.Http.REST { public abstract class RestResource { - [JsonProperty(DefaultValueHandling = DefaultValueHandling.Ignore)] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingDefault)] public virtual int Id { get; set; } [JsonIgnore] diff --git a/src/Radarr.Http/Radarr.Http.csproj b/src/Radarr.Http/Radarr.Http.csproj index d10cd2fe88..e235827b61 100644 --- a/src/Radarr.Http/Radarr.Http.csproj +++ b/src/Radarr.Http/Radarr.Http.csproj @@ -7,7 +7,6 @@ -