mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-06-04 22:24:14 -04:00
JsonSerializer: Cleaning up fields handling
This commit is contained in:
parent
fd50a2dedc
commit
1cd88a8bfe
@ -26,7 +26,6 @@ using Autofac.Extras.AttributeMetadata;
|
|||||||
using Kyoo.Abstractions;
|
using Kyoo.Abstractions;
|
||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models.Utils;
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
using Kyoo.Core.Api;
|
|
||||||
using Kyoo.Core.Controllers;
|
using Kyoo.Core.Controllers;
|
||||||
using Kyoo.Core.Models.Options;
|
using Kyoo.Core.Models.Options;
|
||||||
using Kyoo.Core.Tasks;
|
using Kyoo.Core.Tasks;
|
||||||
@ -35,11 +34,12 @@ using Microsoft.AspNetCore.Builder;
|
|||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Routing;
|
using Microsoft.AspNetCore.Routing;
|
||||||
using Microsoft.AspNetCore.StaticFiles;
|
using Microsoft.AspNetCore.StaticFiles;
|
||||||
using Microsoft.Extensions.Configuration;
|
|
||||||
using Microsoft.Extensions.DependencyInjection;
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
using Microsoft.Extensions.Hosting;
|
using Microsoft.Extensions.Hosting;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
using Serilog;
|
using Serilog;
|
||||||
using IMetadataProvider = Kyoo.Abstractions.Controllers.IMetadataProvider;
|
using IMetadataProvider = Kyoo.Abstractions.Controllers.IMetadataProvider;
|
||||||
|
using JsonOptions = Kyoo.Core.Api.JsonOptions;
|
||||||
|
|
||||||
namespace Kyoo.Core
|
namespace Kyoo.Core
|
||||||
{
|
{
|
||||||
@ -67,20 +67,6 @@ namespace Kyoo.Core
|
|||||||
{ "logging", null }
|
{ "logging", null }
|
||||||
};
|
};
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// The configuration to use.
|
|
||||||
/// </summary>
|
|
||||||
private readonly IConfiguration _configuration;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Create a new core module instance and use the given configuration.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="configuration">The configuration to use</param>
|
|
||||||
public CoreModule(IConfiguration configuration)
|
|
||||||
{
|
|
||||||
_configuration = configuration;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Configure(ContainerBuilder builder)
|
public void Configure(ContainerBuilder builder)
|
||||||
{
|
{
|
||||||
@ -140,17 +126,13 @@ namespace Kyoo.Core
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Configure(IServiceCollection services)
|
public void Configure(IServiceCollection services)
|
||||||
{
|
{
|
||||||
Uri publicUrl = _configuration.GetPublicUrl();
|
services.AddTransient<IConfigureOptions<MvcNewtonsoftJsonOptions>, JsonOptions>();
|
||||||
|
|
||||||
services.AddMvcCore()
|
services.AddMvcCore()
|
||||||
|
.AddNewtonsoftJson()
|
||||||
.AddDataAnnotations()
|
.AddDataAnnotations()
|
||||||
.AddControllersAsServices()
|
.AddControllersAsServices()
|
||||||
.AddApiExplorer()
|
.AddApiExplorer()
|
||||||
.AddNewtonsoftJson(x =>
|
|
||||||
{
|
|
||||||
x.SerializerSettings.ContractResolver = new JsonPropertyIgnorer(publicUrl);
|
|
||||||
x.SerializerSettings.Converters.Add(new PeopleRoleConverter());
|
|
||||||
})
|
|
||||||
.ConfigureApiBehaviorOptions(options =>
|
.ConfigureApiBehaviorOptions(options =>
|
||||||
{
|
{
|
||||||
options.SuppressMapClientErrors = true;
|
options.SuppressMapClientErrors = true;
|
||||||
|
@ -24,6 +24,7 @@ using System.Threading.Tasks;
|
|||||||
using Kyoo.Abstractions.Controllers;
|
using Kyoo.Abstractions.Controllers;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
|
using Kyoo.Abstractions.Models.Utils;
|
||||||
using Kyoo.Utils;
|
using Kyoo.Utils;
|
||||||
using Microsoft.AspNetCore.Mvc;
|
using Microsoft.AspNetCore.Mvc;
|
||||||
using Microsoft.AspNetCore.Mvc.Controllers;
|
using Microsoft.AspNetCore.Mvc.Controllers;
|
||||||
@ -64,13 +65,13 @@ namespace Kyoo.Core.Api
|
|||||||
type = Utility.GetGenericDefinition(type, typeof(ActionResult<>))?.GetGenericArguments()[0] ?? type;
|
type = Utility.GetGenericDefinition(type, typeof(ActionResult<>))?.GetGenericArguments()[0] ?? type;
|
||||||
type = Utility.GetGenericDefinition(type, typeof(Page<>))?.GetGenericArguments()[0] ?? type;
|
type = Utility.GetGenericDefinition(type, typeof(Page<>))?.GetGenericArguments()[0] ?? type;
|
||||||
|
|
||||||
|
context.HttpContext.Items["ResourceType"] = type.Name;
|
||||||
|
|
||||||
PropertyInfo[] properties = type.GetProperties()
|
PropertyInfo[] properties = type.GetProperties()
|
||||||
.Where(x => x.GetCustomAttribute<LoadableRelationAttribute>() != null)
|
.Where(x => x.GetCustomAttribute<LoadableRelationAttribute>() != null)
|
||||||
.ToArray();
|
.ToArray();
|
||||||
if (fields.Count == 1 && fields.Contains("all"))
|
if (fields.Count == 1 && fields.Contains("all"))
|
||||||
{
|
|
||||||
fields = properties.Select(x => x.Name).ToList();
|
fields = properties.Select(x => x.Name).ToList();
|
||||||
}
|
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
fields = fields
|
fields = fields
|
||||||
@ -82,10 +83,9 @@ namespace Kyoo.Core.Api
|
|||||||
?.Name;
|
?.Name;
|
||||||
if (property != null)
|
if (property != null)
|
||||||
return property;
|
return property;
|
||||||
context.Result = new BadRequestObjectResult(new
|
context.Result = new BadRequestObjectResult(
|
||||||
{
|
new RequestError($"{x} does not exist on {type.Name}.")
|
||||||
Error = $"{x} does not exist on {type.Name}."
|
);
|
||||||
});
|
|
||||||
return null;
|
return null;
|
||||||
})
|
})
|
||||||
.ToList();
|
.ToList();
|
||||||
@ -110,7 +110,7 @@ namespace Kyoo.Core.Api
|
|||||||
if (result.DeclaredType == null)
|
if (result.DeclaredType == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ILibraryManager library = context.HttpContext.RequestServices.GetService<ILibraryManager>();
|
ILibraryManager library = context.HttpContext.RequestServices.GetRequiredService<ILibraryManager>();
|
||||||
ICollection<string> fields = (ICollection<string>)context.HttpContext.Items["fields"];
|
ICollection<string> fields = (ICollection<string>)context.HttpContext.Items["fields"];
|
||||||
Type pageType = Utility.GetGenericDefinition(result.DeclaredType, typeof(Page<>));
|
Type pageType = Utility.GetGenericDefinition(result.DeclaredType, typeof(Page<>));
|
||||||
|
|
||||||
@ -119,13 +119,13 @@ namespace Kyoo.Core.Api
|
|||||||
foreach (IResource resource in ((dynamic)result.Value).Items)
|
foreach (IResource resource in ((dynamic)result.Value).Items)
|
||||||
{
|
{
|
||||||
foreach (string field in fields!)
|
foreach (string field in fields!)
|
||||||
await library!.Load(resource, field);
|
await library.Load(resource, field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (result.DeclaredType.IsAssignableTo(typeof(IResource)))
|
else if (result.DeclaredType.IsAssignableTo(typeof(IResource)))
|
||||||
{
|
{
|
||||||
foreach (string field in fields!)
|
foreach (string field in fields!)
|
||||||
await library!.Load((IResource)result.Value, field);
|
await library.Load((IResource)result.Value, field);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
54
src/Kyoo.Core/Views/Helper/Serializers/JsonOptions.cs
Normal file
54
src/Kyoo.Core/Views/Helper/Serializers/JsonOptions.cs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// Kyoo - A portable and vast media library solution.
|
||||||
|
// Copyright (c) Kyoo.
|
||||||
|
//
|
||||||
|
// See AUTHORS.md and LICENSE file in the project root for full license information.
|
||||||
|
//
|
||||||
|
// Kyoo is free software: you can redistribute it and/or modify
|
||||||
|
// it under the terms of the GNU General Public License as published by
|
||||||
|
// the Free Software Foundation, either version 3 of the License, or
|
||||||
|
// any later version.
|
||||||
|
//
|
||||||
|
// Kyoo is distributed in the hope that it will be useful,
|
||||||
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
// GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU General Public License
|
||||||
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
using Microsoft.AspNetCore.Http;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
|
||||||
|
namespace Kyoo.Core.Api
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The custom options of newtonsoft json. This simply add the <see cref="PeopleRoleConverter"/> and set
|
||||||
|
/// the <see cref="JsonSerializerContract"/>. It is on a separate class to use dependency injection.
|
||||||
|
/// </summary>
|
||||||
|
public class JsonOptions : IConfigureOptions<MvcNewtonsoftJsonOptions>
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The http context accessor given to the <see cref="JsonSerializerContract"/>.
|
||||||
|
/// </summary>
|
||||||
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Create a new <see cref="JsonOptions"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="httpContextAccessor">
|
||||||
|
/// The http context accessor given to the <see cref="JsonSerializerContract"/>.
|
||||||
|
/// </param>
|
||||||
|
public JsonOptions(IHttpContextAccessor httpContextAccessor)
|
||||||
|
{
|
||||||
|
_httpContextAccessor = httpContextAccessor;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Configure(MvcNewtonsoftJsonOptions options)
|
||||||
|
{
|
||||||
|
options.SerializerSettings.ContractResolver = new JsonSerializerContract(_httpContextAccessor);
|
||||||
|
options.SerializerSettings.Converters.Add(new PeopleRoleConverter());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,27 +16,39 @@
|
|||||||
// You should have received a copy of the GNU General Public License
|
// You should have received a copy of the GNU General Public License
|
||||||
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
// along with Kyoo. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Kyoo.Abstractions.Models;
|
using Kyoo.Abstractions.Models;
|
||||||
using Kyoo.Abstractions.Models.Attributes;
|
using Kyoo.Abstractions.Models.Attributes;
|
||||||
using Kyoo.Utils;
|
using Microsoft.AspNetCore.Http;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
using Newtonsoft.Json.Serialization;
|
using Newtonsoft.Json.Serialization;
|
||||||
|
|
||||||
namespace Kyoo.Core.Api
|
namespace Kyoo.Core.Api
|
||||||
{
|
{
|
||||||
public class JsonPropertyIgnorer : CamelCasePropertyNamesContractResolver
|
/// <summary>
|
||||||
|
/// A custom json serializer that respects <see cref="SerializeIgnoreAttribute"/> and
|
||||||
|
/// <see cref="DeserializeIgnoreAttribute"/>. It also handle <see cref="LoadableRelationAttribute"/> via the
|
||||||
|
/// <c>fields</c> query parameter and <see cref="IThumbnails"/> items.
|
||||||
|
/// </summary>
|
||||||
|
public class JsonSerializerContract : CamelCasePropertyNamesContractResolver
|
||||||
{
|
{
|
||||||
private readonly Uri _host;
|
/// <summary>
|
||||||
private int _depth = -1;
|
/// The http context accessor used to retrieve the <c>fields</c> query parameter as well as the type of
|
||||||
|
/// resource currently serializing.
|
||||||
|
/// </summary>
|
||||||
|
private readonly IHttpContextAccessor _httpContextAccessor;
|
||||||
|
|
||||||
public JsonPropertyIgnorer(Uri host)
|
/// <summary>
|
||||||
|
/// Create a new <see cref="JsonSerializerContract"/>.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="httpContextAccessor">The http context accessor to use.</param>
|
||||||
|
public JsonSerializerContract(IHttpContextAccessor httpContextAccessor)
|
||||||
{
|
{
|
||||||
_host = host;
|
_httpContextAccessor = httpContextAccessor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
protected override JsonProperty CreateProperty(MemberInfo member, MemberSerialization memberSerialization)
|
||||||
{
|
{
|
||||||
JsonProperty property = base.CreateProperty(member, memberSerialization);
|
JsonProperty property = base.CreateProperty(member, memberSerialization);
|
||||||
@ -44,20 +56,15 @@ namespace Kyoo.Core.Api
|
|||||||
LoadableRelationAttribute relation = member.GetCustomAttribute<LoadableRelationAttribute>();
|
LoadableRelationAttribute relation = member.GetCustomAttribute<LoadableRelationAttribute>();
|
||||||
if (relation != null)
|
if (relation != null)
|
||||||
{
|
{
|
||||||
if (relation.RelationID == null)
|
property.ShouldSerialize = _ =>
|
||||||
property.ShouldSerialize = x => _depth == 0 && member.GetValue(x) != null;
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
property.ShouldSerialize = x =>
|
string resType = (string)_httpContextAccessor.HttpContext!.Items["ResourceType"];
|
||||||
{
|
if (member.DeclaringType!.Name != resType)
|
||||||
if (_depth != 0)
|
|
||||||
return false;
|
return false;
|
||||||
if (member.GetValue(x) != null)
|
ICollection<string> fields = (ICollection<string>)_httpContextAccessor.HttpContext!.Items["fields"];
|
||||||
return true;
|
return fields!.Contains(member.Name);
|
||||||
return x.GetType().GetProperty(relation.RelationID)?.GetValue(x) != null;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (member.GetCustomAttribute<SerializeIgnoreAttribute>() != null)
|
if (member.GetCustomAttribute<SerializeIgnoreAttribute>() != null)
|
||||||
property.ShouldSerialize = _ => false;
|
property.ShouldSerialize = _ => false;
|
||||||
@ -66,24 +73,10 @@ namespace Kyoo.Core.Api
|
|||||||
|
|
||||||
// TODO use http context to disable serialize as.
|
// TODO use http context to disable serialize as.
|
||||||
// TODO check https://stackoverflow.com/questions/53288633/net-core-api-custom-json-resolver-based-on-request-values
|
// TODO check https://stackoverflow.com/questions/53288633/net-core-api-custom-json-resolver-based-on-request-values
|
||||||
SerializeAsAttribute serializeAs = member.GetCustomAttribute<SerializeAsAttribute>();
|
// SerializeAsAttribute serializeAs = member.GetCustomAttribute<SerializeAsAttribute>();
|
||||||
if (serializeAs != null)
|
// if (serializeAs != null)
|
||||||
property.ValueProvider = new SerializeAsProvider(serializeAs.Format, _host);
|
// property.ValueProvider = new SerializeAsProvider(serializeAs.Format, _host);
|
||||||
return property;
|
return property;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override JsonContract CreateContract(Type objectType)
|
|
||||||
{
|
|
||||||
JsonContract contract = base.CreateContract(objectType);
|
|
||||||
if (Utility.GetGenericDefinition(objectType, typeof(Page<>)) == null
|
|
||||||
&& !objectType.IsAssignableTo(typeof(IEnumerable))
|
|
||||||
&& objectType.Name != "AnnotatedProblemDetails")
|
|
||||||
{
|
|
||||||
contract.OnSerializingCallbacks.Add((_, _) => _depth++);
|
|
||||||
contract.OnSerializedCallbacks.Add((_, _) => _depth--);
|
|
||||||
}
|
|
||||||
|
|
||||||
return contract;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,12 +1,8 @@
|
|||||||
<Project Sdk="Microsoft.NET.Sdk">
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<TargetFramework>net5.0</TargetFramework>
|
<TargetFramework>net5.0</TargetFramework>
|
||||||
<LangVersion>default</LangVersion>
|
<LangVersion>default</LangVersion>
|
||||||
<IsPackable>false</IsPackable>
|
<IsPackable>false</IsPackable>
|
||||||
|
|
||||||
<Authors>Zoe Roux</Authors>
|
|
||||||
<Company>SDG</Company>
|
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
@ -37,5 +33,4 @@
|
|||||||
<ProjectReference Include="../../src/Kyoo.Core/Kyoo.Core.csproj" />
|
<ProjectReference Include="../../src/Kyoo.Core/Kyoo.Core.csproj" />
|
||||||
<ProjectReference Include="../../src/Kyoo.TheTvdb/Kyoo.TheTvdb.csproj" />
|
<ProjectReference Include="../../src/Kyoo.TheTvdb/Kyoo.TheTvdb.csproj" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user