Skip to content

Commit 2a2e52a

Browse files
committed
Move JsonConverters, support TypeConverter, fix custom tool settings load
1 parent 7626043 commit 2a2e52a

7 files changed

Lines changed: 84 additions & 38 deletions

File tree

src/BizHawk.Client.Common/config/ConfigService.cs

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,13 @@
1-
using System.Globalization;
21
using System.IO;
32
using System.Text.Encodings.Web;
43
using System.Text.Json;
54
using System.Text.Json.Nodes;
6-
using System.Text.Json.Serialization;
75

86
using BizHawk.Common;
9-
using BizHawk.Emulation.Common;
7+
using BizHawk.Emulation.Common.Json;
108

119
namespace BizHawk.Client.Common
1210
{
13-
internal class FloatConverter : JsonConverter<float>
14-
{
15-
public override float Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => reader.GetSingle();
16-
17-
public override void Write(Utf8JsonWriter writer, float value, JsonSerializerOptions options)
18-
{
19-
#if NETCOREAPP
20-
writer.WriteNumberValue(value);
21-
#else
22-
// gotta love the fact .net framework can't even format floats correctly by default
23-
// can't use G7 here because it may be too low accuracy, and can't use G8 because it may be too high, see 1.0000003f or 0.8f
24-
writer.WriteRawValue(value.ToString("R", NumberFormatInfo.InvariantInfo));
25-
#endif
26-
}
27-
}
28-
2911
public static class ConfigService
3012
{
3113
private static readonly JsonSerializerOptions NonIndentedSerializerOptions = new()
@@ -36,7 +18,8 @@ public static class ConfigService
3618
Converters =
3719
{
3820
new FloatConverter(), // this serializes floats with minimum required precision, e.g. 1.8000000012 -> 1.8
39-
new ByteArrayAsNormalArrayJsonConverter() // this preserves the old behaviour of e,g, 0x1234ABCD --> [18,52,171,205]; omitting it will use base64 e.g. "EjSrzQ=="
21+
new ByteArrayAsNormalArrayJsonConverter(), // this preserves the old behaviour of e,g, 0x1234ABCD --> [18,52,171,205]; omitting it will use base64 e.g. "EjSrzQ=="
22+
new TypeConverterJsonAdapterFactory(), // allows serialization using `[TypeConverter]` attributes
4023
}
4124
};
4225

@@ -45,7 +28,7 @@ public static class ConfigService
4528
WriteIndented = true
4629
};
4730

48-
internal static JsonSerializerOptions SerializerOptions => NonIndentedSerializerOptions;
31+
public static JsonSerializerOptions SerializerOptions => NonIndentedSerializerOptions;
4932

5033
public static bool IsFromSameVersion(string filepath, out string msg)
5134
{

src/BizHawk.Client.EmuHawk/tools/ToolManager.cs

Lines changed: 3 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
using System.IO;
55
using System.Linq;
66
using System.Reflection;
7-
using System.ComponentModel;
7+
using System.Text.Json;
88
using System.Windows.Forms;
99

1010
using BizHawk.Client.Common;
@@ -401,21 +401,9 @@ private static void InstallCustomConfig(IToolForm tool, Dictionary<string, objec
401401
{
402402
if (data.TryGetValue(prop.Name, out var val))
403403
{
404-
if (val is string str && prop.PropertyType != typeof(string))
404+
if (val is JsonElement jsonElement)
405405
{
406-
// if a type has a TypeConverter, and that converter can convert to string,
407-
// that will be used in place of object markup by JSON.NET
408-
409-
// but that doesn't work with $type metadata, and JSON.NET fails to fall
410-
// back on regular object serialization when needed. so try to undo a TypeConverter
411-
// operation here
412-
var converter = TypeDescriptor.GetConverter(prop.PropertyType);
413-
val = converter.ConvertFromString(null, CultureInfo.InvariantCulture, str);
414-
}
415-
else if (val is not bool && prop.PropertyType.IsPrimitive)
416-
{
417-
// numeric constants are similarly hosed
418-
val = Convert.ChangeType(val, prop.PropertyType, CultureInfo.InvariantCulture);
406+
val = jsonElement.Deserialize(prop.PropertyType, ConfigService.SerializerOptions);
419407
}
420408

421409
prop.SetValue(tool, val, null);

src/BizHawk.Emulation.Common/Array2DJsonConverter.cs renamed to src/BizHawk.Emulation.Common/Json/Array2DJsonConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
using System.Text.Json;
44
using System.Text.Json.Serialization;
55

6-
namespace BizHawk.Emulation.Common
6+
namespace BizHawk.Emulation.Common.Json
77
{
88
// heavily inspired by https://stackoverflow.com/questions/66280645/how-can-i-serialize-a-double-2d-array-to-json-using-system-text-json
99
public class Array2DJsonConverter<T> : JsonConverter<T[,]?>

src/BizHawk.Emulation.Common/ByteArrayAsNormalArrayJsonConverter.cs renamed to src/BizHawk.Emulation.Common/Json/ByteArrayAsNormalArrayJsonConverter.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
using System.Text.Json;
33
using System.Text.Json.Serialization;
44

5-
namespace BizHawk.Emulation.Common
5+
namespace BizHawk.Emulation.Common.Json
66
{
77
/// <remarks>based on <see href="https://stackoverflow.com/a/15228384">this SO answer</see></remarks>
88
public sealed class ByteArrayAsNormalArrayJsonConverter : JsonConverter<byte[]?>
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
using System.Globalization;
2+
using System.Text.Json;
3+
using System.Text.Json.Serialization;
4+
5+
namespace BizHawk.Emulation.Common.Json
6+
{
7+
public class FloatConverter : JsonConverter<float>
8+
{
9+
public override float Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => reader.GetSingle();
10+
11+
public override void Write(Utf8JsonWriter writer, float value, JsonSerializerOptions options)
12+
{
13+
#if NETCOREAPP
14+
writer.WriteNumberValue(value);
15+
#else
16+
// gotta love the fact .net framework can't even format floats correctly by default
17+
// can't use G7 here because it may be too low accuracy, and can't use G8 because it may be too high, see 1.0000003f or 0.8f
18+
writer.WriteRawValue(value.ToString("R", NumberFormatInfo.InvariantInfo));
19+
#endif
20+
}
21+
}
22+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
using System.ComponentModel;
2+
using System.Linq;
3+
using System.Reflection;
4+
using System.Text.Json;
5+
using System.Text.Json.Serialization;
6+
7+
// System.Text.Json does not respect `[TypeConverter]`s by default, so it's required to use a JsonConverter that acts as an adapter
8+
// see also https://github.com/dotnet/runtime/issues/38812 or https://github.com/dotnet/runtime/issues/1761
9+
namespace BizHawk.Emulation.Common.Json
10+
{
11+
/// <summary>
12+
/// Adapter between <see cref="TypeConverter"/> and <see cref="JsonConverter"/>.
13+
/// </summary>
14+
public class TypeConverterJsonAdapter<T> : JsonConverter<T>
15+
{
16+
/// <inheritdoc/>
17+
public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
18+
{
19+
var converter = TypeDescriptor.GetConverter(typeToConvert);
20+
return (T)converter.ConvertFromString(reader.GetString())!;
21+
}
22+
23+
/// <inheritdoc/>
24+
public override void Write(Utf8JsonWriter writer, T objectToWrite, JsonSerializerOptions options)
25+
{
26+
var converter = TypeDescriptor.GetConverter(objectToWrite!);
27+
writer.WriteStringValue(converter.ConvertToString(objectToWrite!));
28+
}
29+
30+
/// <inheritdoc/>
31+
public override bool CanConvert(Type typeToConvert) => typeToConvert.GetCustomAttributes<TypeConverterAttribute>(inherit: true).Any();
32+
}
33+
34+
/// <inheritdoc />
35+
public class TypeConverterJsonAdapter : TypeConverterJsonAdapter<object>;
36+
37+
/// <summary>
38+
/// A factory used to create various <see cref="TypeConverterJsonAdapter{T}"/> instances.
39+
/// </summary>
40+
public class TypeConverterJsonAdapterFactory : JsonConverterFactory
41+
{
42+
/// <inheritdoc />
43+
public override bool CanConvert(Type typeToConvert) => typeToConvert.GetCustomAttributes<TypeConverterAttribute>(inherit: true).Any();
44+
45+
/// <inheritdoc />
46+
public override JsonConverter CreateConverter(Type typeToConvert, JsonSerializerOptions options)
47+
{
48+
var converterType = typeof(TypeConverterJsonAdapter<>).MakeGenericType(typeToConvert);
49+
return (JsonConverter)Activator.CreateInstance(converterType);
50+
}
51+
}
52+
}

src/BizHawk.Emulation.Cores/Consoles/Nintendo/NES/NES.ISettable.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
using BizHawk.Common;
66
using BizHawk.Emulation.Common;
7+
using BizHawk.Emulation.Common.Json;
78

89
namespace BizHawk.Emulation.Cores.Nintendo.NES
910
{

0 commit comments

Comments
 (0)