1
0
mirror of https://github.com/ppy/osu.git synced 2024-11-11 09:07:52 +08:00

Rework logic of TypedListConverter to remove extra converter for bindables

This commit is contained in:
tsunyoku 2023-10-15 17:23:00 +01:00
parent 6c8f4addb8
commit cae5677605
3 changed files with 18 additions and 122 deletions

View File

@ -22,7 +22,7 @@ namespace osu.Game.Beatmaps.ControlPoints
[JsonIgnore]
public IBindableList<ControlPoint> ControlPoints => controlPoints;
[JsonConverter(typeof(BindableListConverter<ControlPoint>))]
[JsonConverter(typeof(TypedListConverter<ControlPoint>))]
[JsonProperty]
private readonly BindableList<ControlPoint> controlPoints = new BindableList<ControlPoint>();

View File

@ -1,114 +0,0 @@
// Copyright (c) ppy Pty Ltd <contact@ppy.sh>. Licensed under the MIT Licence.
// See the LICENCE file in the repository root for full licence text.
#nullable disable
using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
namespace osu.Game.IO.Serialization.Converters
{
/// <summary>
/// A type of <see cref="JsonConverter"/> that serializes an <see cref="IBindableList{T}"/> alongside
/// a lookup table for the types contained. The lookup table is used in deserialization to
/// reconstruct the objects with their original types.
/// </summary>
/// <typeparam name="T">The type of objects contained in the <see cref="IBindableList{T}"/> this attribute is attached to.</typeparam>
public class BindableListConverter<T> : JsonConverter<IBindableList<T>>
{
private readonly bool requiresTypeVersion;
/// <summary>
/// Constructs a new <see cref="BindableListConverter{T}"/>.
/// </summary>
// ReSharper disable once UnusedMember.Global
public BindableListConverter()
{
}
/// <summary>
/// Constructs a new <see cref="BindableListConverter{T}"/>.
/// </summary>
/// <param name="requiresTypeVersion">Whether the version of the type should be serialized.</param>
// ReSharper disable once UnusedMember.Global (Used in Beatmap)
public BindableListConverter(bool requiresTypeVersion)
{
this.requiresTypeVersion = requiresTypeVersion;
}
public override IBindableList<T> ReadJson(JsonReader reader, Type objectType, IBindableList<T> existingValue, bool hasExistingValue, JsonSerializer serializer)
{
var list = new BindableList<T>();
var obj = JObject.Load(reader);
if (obj["$lookup_table"] == null)
return list;
var lookupTable = serializer.Deserialize<List<string>>(obj["$lookup_table"].CreateReader());
if (lookupTable == null)
return list;
if (obj["$items"] == null)
return list;
foreach (var tok in obj["$items"])
{
var itemReader = tok.CreateReader();
if (tok["$type"] == null)
throw new JsonException("Expected $type token.");
string typeName = lookupTable[(int)tok["$type"]];
var instance = (T)Activator.CreateInstance(Type.GetType(typeName).AsNonNull())!;
serializer.Populate(itemReader, instance);
list.Add(instance);
}
return list;
}
public override void WriteJson(JsonWriter writer, IBindableList<T> value, JsonSerializer serializer)
{
var lookupTable = new List<string>();
var objects = new List<JObject>();
foreach (var item in value)
{
var type = item.GetType();
var assemblyName = type.Assembly.GetName();
string typeString = $"{type.FullName}, {assemblyName.Name}";
if (requiresTypeVersion)
typeString += $", {assemblyName.Version}";
int typeId = lookupTable.IndexOf(typeString);
if (typeId == -1)
{
lookupTable.Add(typeString);
typeId = lookupTable.Count - 1;
}
var itemObject = JObject.FromObject(item, serializer);
itemObject.AddFirst(new JProperty("$type", typeId));
objects.Add(itemObject);
}
writer.WriteStartObject();
writer.WritePropertyName("$lookup_table");
serializer.Serialize(writer, lookupTable);
writer.WritePropertyName("$items");
serializer.Serialize(writer, objects);
writer.WriteEndObject();
}
}
}

View File

@ -7,17 +7,18 @@ using System;
using System.Collections.Generic;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using osu.Framework.Bindables;
using osu.Framework.Extensions.ObjectExtensions;
namespace osu.Game.IO.Serialization.Converters
{
/// <summary>
/// A type of <see cref="JsonConverter"/> that serializes an <see cref="IReadOnlyList{T}"/> alongside
/// A type of <see cref="JsonConverter"/> that serializes a list alongside
/// a lookup table for the types contained. The lookup table is used in deserialization to
/// reconstruct the objects with their original types.
/// </summary>
/// <typeparam name="T">The type of objects contained in the <see cref="IReadOnlyList{T}"/> this attribute is attached to.</typeparam>
public class TypedListConverter<T> : JsonConverter<IReadOnlyList<T>>
/// <typeparam name="T">The type of objects contained in the list this attribute is attached to.</typeparam>
public class TypedListConverter<T> : JsonConverter
{
private readonly bool requiresTypeVersion;
@ -39,10 +40,9 @@ namespace osu.Game.IO.Serialization.Converters
this.requiresTypeVersion = requiresTypeVersion;
}
public override IReadOnlyList<T> ReadJson(JsonReader reader, Type objectType, IReadOnlyList<T> existingValue, bool hasExistingValue, JsonSerializer serializer)
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
{
var list = new List<T>();
var obj = JObject.Load(reader);
if (obj["$lookup_table"] == null)
@ -69,15 +69,25 @@ namespace osu.Game.IO.Serialization.Converters
list.Add(instance);
}
if (objectType == typeof(IBindableList<T>) || objectType == typeof(BindableList<T>))
return new BindableList<T>(list);
if (objectType == typeof(IReadOnlyList<T>))
return list.AsReadOnly();
return list;
}
public override void WriteJson(JsonWriter writer, IReadOnlyList<T> value, JsonSerializer serializer)
public override bool CanConvert(Type objectType)
=> objectType == typeof(IBindableList<T>) || objectType == typeof(BindableList<T>) || objectType == typeof(IReadOnlyList<T>) || objectType == typeof(List<T>);
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
{
var enumerable = (IEnumerable<T>)value!;
var lookupTable = new List<string>();
var objects = new List<JObject>();
foreach (var item in value)
foreach (var item in enumerable)
{
var type = item.GetType();
var assemblyName = type.Assembly.GetName();