Reworking the json serializer/deserializer to support the api's edit

This commit is contained in:
Zoe Roux 2020-11-01 04:35:08 +01:00
parent 0f5cc8f3a1
commit fb907509cb
26 changed files with 71 additions and 55 deletions

View File

@ -16,7 +16,6 @@
<ItemGroup>
<PackageReference Include="JetBrains.Annotations" Version="2020.1.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" />
</ItemGroup>
</Project>

View File

@ -8,4 +8,7 @@ namespace Kyoo.Models.Attributes
{
void OnMerge(object merged);
}
public class JsonReadOnly : Attribute { }
public class JsonIgnore : JsonReadOnly { }
}

View File

@ -1,4 +1,4 @@
using Newtonsoft.Json;
using Kyoo.Models.Attributes;
namespace Kyoo.Models
{

View File

@ -1,7 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq.Expressions;
using Newtonsoft.Json;
using Kyoo.Models.Attributes;
namespace Kyoo.Models
{

View File

@ -1,5 +1,5 @@
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Collections.Generic;
using Kyoo.Models.Attributes;
namespace Kyoo.Models
{

View File

@ -1,5 +1,4 @@
using Newtonsoft.Json;
using System;
using System;
using System.Collections.Generic;
using Kyoo.Models.Attributes;

View File

@ -1,5 +1,5 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Kyoo.Models.Attributes;
namespace Kyoo.Models
{

View File

@ -1,5 +1,5 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Kyoo.Models.Attributes;
namespace Kyoo.Models
{

View File

@ -1,6 +1,5 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using Kyoo.Models.Attributes;
namespace Kyoo.Models
{
@ -12,7 +11,7 @@ namespace Kyoo.Models
public string Poster { get; set; }
public virtual IEnumerable<MetadataID> ExternalIDs { get; set; }
[JsonIgnore] public virtual IEnumerable<PeopleRole> Roles { get; set; }
[JsonReadOnly] public virtual IEnumerable<PeopleRole> Roles { get; set; }
public People() {}

View File

@ -1,4 +1,4 @@
using Newtonsoft.Json;
using Kyoo.Models.Attributes;
namespace Kyoo.Models
{

View File

@ -1,5 +1,5 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Kyoo.Models.Attributes;
namespace Kyoo.Models
{

View File

@ -1,5 +1,4 @@
using Newtonsoft.Json;
using System.Collections.Generic;
using System.Collections.Generic;
using System.Linq;
using Kyoo.Models.Attributes;
@ -29,9 +28,9 @@ namespace Kyoo.Models
[JsonIgnore] public int? StudioID { get; set; }
[JsonIgnore] public virtual Studio Studio { get; set; }
[JsonIgnore] public virtual IEnumerable<Genre> Genres { get; set; }
[JsonIgnore] public virtual IEnumerable<PeopleRole> People { get; set; }
[JsonReadOnly] public virtual Studio Studio { get; set; }
[JsonReadOnly] public virtual IEnumerable<Genre> Genres { get; set; }
[JsonReadOnly] public virtual IEnumerable<PeopleRole> People { get; set; }
[JsonIgnore] public virtual IEnumerable<Season> Seasons { get; set; }
[JsonIgnore] public virtual IEnumerable<Episode> Episodes { get; set; }
[JsonIgnore] public virtual IEnumerable<Library> Libraries { get; set; }

View File

@ -1,5 +1,5 @@
using System.Collections.Generic;
using Newtonsoft.Json;
using Kyoo.Models.Attributes;
namespace Kyoo.Models
{

View File

@ -1,8 +1,8 @@
using Kyoo.Models.Watch;
using Newtonsoft.Json;
using System.Globalization;
using System.Linq;
using System.Runtime.InteropServices;
using Kyoo.Models.Attributes;
namespace Kyoo.Models
{

View File

@ -1,10 +1,10 @@
using Newtonsoft.Json;
using System;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using Kyoo.Controllers;
using Kyoo.Models.Attributes;
using Kyoo.Models.Watch;
using PathIO = System.IO.Path;

View File

@ -112,9 +112,13 @@ namespace Kyoo
object defaultValue = property.PropertyType.IsValueType
? Activator.CreateInstance(property.PropertyType)
: null;
if (value?.Equals(defaultValue) == false)
{
if (value is IEnumerable enumerable && !(value is string))
value = RunGenericMethod(typeof(Enumerable), "ToList", GetEnumerableType(enumerable), value);
property.SetValue(first, value);
}
}
if (first is IOnMerge merge)

View File

@ -3,11 +3,11 @@ using System.Buffers;
using System.Collections.Generic;
using System.Reflection;
using Kyoo.Models;
using Kyoo.Models.Attributes;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.AspNetCore.Mvc.Formatters;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Newtonsoft.Json;
using Newtonsoft.Json.Serialization;
@ -24,7 +24,8 @@ namespace Kyoo.Controllers
_forceSerialize = new Dictionary<Type, HashSet<string>>();
}
public JsonPropertySelector(Dictionary<Type, HashSet<string>> ignored, Dictionary<Type, HashSet<string>> forceSerialize)
public JsonPropertySelector(Dictionary<Type, HashSet<string>> ignored,
Dictionary<Type, HashSet<string>> forceSerialize = null)
{
_ignored = ignored ?? new Dictionary<Type, HashSet<string>>();
_forceSerialize = forceSerialize ?? new Dictionary<Type, HashSet<string>>();
@ -58,15 +59,20 @@ namespace Kyoo.Controllers
{
JsonProperty property = base.CreateProperty(member, memberSerialization);
if (IsIgnored(property.DeclaringType, property.PropertyName))
if (IsSerializationForced(property.DeclaringType, property.PropertyName))
{
property.ShouldSerialize = i => true;
property.Ignored = false;
}
else if (IsIgnored(property.DeclaringType, property.PropertyName))
{
property.ShouldSerialize = i => false;
property.Ignored = true;
}
else if (IsSerializationForced(property.DeclaringType, property.PropertyName))
else
{
property.ShouldSerialize = i => true;
property.Ignored = false;
property.ShouldSerialize = i => member.GetCustomAttribute<JsonReadOnly>(true) == null;
property.ShouldDeserialize = i => member.GetCustomAttribute<JsonIgnore>(true) == null;
}
return property;
}
@ -89,7 +95,7 @@ namespace Kyoo.Controllers
})
},
context.HttpContext.RequestServices.GetRequiredService<ArrayPool<char>>(),
context.HttpContext.RequestServices.GetRequiredService<IOptions<MvcOptions>>().Value));
new MvcOptions()));
}
}
}

View File

@ -10,6 +10,7 @@ using Kyoo.CommonApi;
using Kyoo.Models;
using Kyoo.Models.Exceptions;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.ChangeTracking;
namespace Kyoo.Controllers
{
@ -142,18 +143,30 @@ namespace Kyoo.Controllers
{
if (edited == null)
throw new ArgumentNullException(nameof(edited));
T old = await Get(edited.Slug);
if (old == null)
throw new ItemNotFound($"No resource found with the slug {edited.Slug}.");
if (resetOld)
Utility.Nullify(old);
Utility.Merge(old, edited);
await Validate(old);
await Database.SaveChangesAsync();
return old;
Database.ChangeTracker.LazyLoadingEnabled = false;
try
{
T old = await Get(edited.ID);
if (old == null)
throw new ItemNotFound($"No resource found with the ID {edited.ID}.");
if (resetOld)
Utility.Nullify(old);
Utility.Complete(old, edited);
await Validate(old);
// TODO should fix this, new links & deleted links should be kept.
// TODO The changetracker has trash values now & values can't be listed before the validation (exception is thrown)
foreach (EntityEntry x in Database.ChangeTracker.Entries().Where(x => x.Entity != old))
x.State = EntityState.Detached;
await Database.SaveChangesAsync();
return old;
}
finally
{
Database.ChangeTracker.LazyLoadingEnabled = true;
}
}
protected virtual Task Validate(T resource)

View File

@ -16,6 +16,7 @@
<Authors>Anonymus-Raccoon</Authors>
<RepositoryUrl>https://github.com/AnonymusRaccoon/Kyoo</RepositoryUrl>
<StartupObject>Kyoo.Program</StartupObject>
<LangVersion>default</LangVersion>
</PropertyGroup>
<ItemGroup>

View File

@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using Kyoo.Models.Attributes;
using Newtonsoft.Json;
namespace Kyoo.Models
{

View File

@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using Kyoo.Models.Attributes;
using Newtonsoft.Json;
namespace Kyoo.Models
{

View File

@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using Kyoo.Models.Attributes;
using Newtonsoft.Json;
namespace Kyoo.Models
{

View File

@ -1,16 +1,12 @@
using System.Collections.Generic;
using System.Linq;
using Kyoo.Models.Attributes;
using Newtonsoft.Json;
// TODO Remove every [JsonIgnore] tag from here once the serializer knows which property should be serialized.
namespace Kyoo.Models
{
public class ShowDE : Show
{
[JsonIgnore] [NotMergable] public virtual ICollection<GenreLink> GenreLinks { get; set; }
[JsonReadOnly] [NotMergable] public virtual ICollection<GenreLink> GenreLinks { get; set; }
[ExpressionRewrite(nameof(GenreLinks), nameof(GenreLink.Genre))]
public override IEnumerable<Genre> Genres
{
@ -18,7 +14,7 @@ namespace Kyoo.Models
set => GenreLinks = value?.Select(x => new GenreLink(this, x)).ToList();
}
[JsonIgnore] [NotMergable] public virtual ICollection<LibraryLink> LibraryLinks { get; set; }
[JsonReadOnly] [NotMergable] public virtual ICollection<LibraryLink> LibraryLinks { get; set; }
[ExpressionRewrite(nameof(LibraryLinks), nameof(LibraryLink.Library))]
public override IEnumerable<Library> Libraries
{
@ -26,7 +22,7 @@ namespace Kyoo.Models
set => LibraryLinks = value?.Select(x => new LibraryLink(x, this)).ToList();
}
[JsonIgnore] [NotMergable] public virtual ICollection<CollectionLink> CollectionLinks { get; set; }
[JsonReadOnly] [NotMergable] public virtual ICollection<CollectionLink> CollectionLinks { get; set; }
[ExpressionRewrite(nameof(CollectionLinks), nameof(CollectionLink.Collection))]
public override IEnumerable<Collection> Collections
{

View File

@ -1,5 +1,4 @@
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.AspNetCore;
using Microsoft.AspNetCore.Hosting;

View File

@ -40,7 +40,8 @@ namespace Kyoo
configuration.RootPath = "wwwroot";
});
services.AddControllers().AddNewtonsoftJson();
services.AddControllers()
.AddNewtonsoftJson(x => x.SerializerSettings.ContractResolver = new JsonPropertySelector());
services.AddHttpClient();
services.AddDbContext<DatabaseContext>(options =>

@ -1 +1 @@
Subproject commit 42af654da4f4ec45945336e6f1085d9bda8ed773
Subproject commit f2ee4854ab7531cb8cf38ed9fd3d2b97d1d04a97