Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Switch from Newtonsoft.Json to System.Text.Json #3932

Open
wants to merge 8 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
<PackageVersion Include="System.Reflection.Emit" Version="4.7.0" />
<PackageVersion Include="System.Resources.Extensions" Version="7.0.0" />
<PackageVersion Include="System.Runtime.CompilerServices.Unsafe" Version="6.0.0" />
<PackageVersion Include="System.Text.Json" Version="8.0.4" />
<PackageVersion Include="Vortice.Direct3D11" Version="2.4.2" /> <!-- last version with .NET Standard 2.0 support -->
<PackageVersion Include="Vortice.D3DCompiler" Version="2.4.2" /> <!-- last version with .NET Standard 2.0 support -->
<PackageVersion Include="Vortice.MediaFoundation" Version="2.4.2" /> <!-- last version with .NET Standard 2.0 support -->
Expand Down
9 changes: 4 additions & 5 deletions src/BizHawk.Client.Common/OpenAdvanced.cs
Original file line number Diff line number Diff line change
@@ -1,9 +1,8 @@
using System.IO;
using System.Text.Json;

using BizHawk.Common.StringExtensions;

using Newtonsoft.Json;

//this file contains some cumbersome self-"serialization" in order to gain a modicum of control over what the serialized output looks like
//I don't want them to look like crufty json

Expand Down Expand Up @@ -94,12 +93,12 @@ public struct Token

public void Deserialize(string str)
{
token = JsonConvert.DeserializeObject<Token>(str);
token = JsonSerializer.Deserialize<Token>(str);
}

public void Serialize(TextWriter tw)
{
tw.Write(JsonConvert.SerializeObject(token));
tw.Write(JsonSerializer.Serialize(token));
}

public string CorePath
Expand Down Expand Up @@ -186,4 +185,4 @@ public void Serialize(TextWriter tw)
tw.Write(Path);
}
}
}
}
4 changes: 3 additions & 1 deletion src/BizHawk.Client.Common/RecentFiles.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System.Collections.Generic;
using System.Linq;
using Newtonsoft.Json;
using System.Text.Json.Serialization;

namespace BizHawk.Client.Common
{
public class RecentFiles
{
[JsonInclude]
[JsonPropertyOrder(-1)]
// ReSharper disable once FieldCanBeMadeReadOnly.Local
private List<string> recentlist;

Expand Down
12 changes: 7 additions & 5 deletions src/BizHawk.Client.Common/config/Config.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Text.Json;
using System.Text.Json.Serialization;

using BizHawk.Bizware.Graphics;
using BizHawk.Common;
Expand All @@ -9,9 +11,6 @@
using BizHawk.Emulation.Common;
using BizHawk.Emulation.Cores;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;

namespace BizHawk.Client.Common
{
public class Config
Expand Down Expand Up @@ -368,8 +367,10 @@ public void SetWindowScaleFor(string sysID, int windowScale)
public bool VideoWriterAudioSyncEffective;

// Emulation core settings
internal Dictionary<string, JToken> CoreSettings { get; set; } = new Dictionary<string, JToken>();
internal Dictionary<string, JToken> CoreSyncSettings { get; set; } = new Dictionary<string, JToken>();
[JsonInclude]
internal Dictionary<string, JsonElement> CoreSettings { get; set; } = new();
[JsonInclude]
internal Dictionary<string, JsonElement> CoreSyncSettings { get; set; } = new();

public Dictionary<string, ToolDialogSettings> CommonToolSettings { get; set; } = new Dictionary<string, ToolDialogSettings>();
public Dictionary<string, Dictionary<string, object>> CustomToolSettings { get; set; } = new Dictionary<string, Dictionary<string, object>>();
Expand Down Expand Up @@ -405,6 +406,7 @@ public void SetWindowScaleFor(string sysID, int windowScale)
public bool GbAsSgb { get; set; }
public string LibretroCore { get; set; }

[JsonPropertyOrder(-1)]
public Dictionary<string, string> PreferredCores = GenDefaultCorePreferences();

public bool DontTryOtherCores { get; set; }
Expand Down
20 changes: 5 additions & 15 deletions src/BizHawk.Client.Common/config/ConfigExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,33 +1,23 @@
using System.Collections.Generic;
using System.Linq;
using System.Text.Json;

using BizHawk.Common.StringExtensions;
using BizHawk.Emulation.Common;

using Newtonsoft.Json.Linq;

namespace BizHawk.Client.Common
{
public static class ConfigExtensions
{
private class TypeNameEncapsulator
{
public object o;
}
private static JToken Serialize(object o)
private static JsonElement Serialize(object o)
{
var tne = new TypeNameEncapsulator { o = o };
return JToken.FromObject(tne, ConfigService.Serializer)["o"];

// Maybe todo: This code is identical to the code above, except that it does not emit the legacy "$type"
// parameter that we no longer need here. Leaving that in to make bisecting during this dev phase easier, and such.
// return JToken.FromObject(o, ConfigService.Serializer);
return JsonSerializer.SerializeToElement(o, ConfigService.SerializerOptions);
}
private static object Deserialize(JToken j, Type type)
private static object Deserialize(JsonElement json, Type type)
{
try
{
return j?.ToObject(type, ConfigService.Serializer);
return json.Deserialize(type, ConfigService.SerializerOptions);
}
catch
{
Expand Down
89 changes: 44 additions & 45 deletions src/BizHawk.Client.Common/config/ConfigService.cs
Original file line number Diff line number Diff line change
@@ -1,37 +1,34 @@
using System.IO;
using System.Reflection;
using System.Text.Encodings.Web;
using System.Text.Json;
using System.Text.Json.Nodes;

using BizHawk.Common;

using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;

#pragma warning disable 618
using BizHawk.Emulation.Common.Json;

namespace BizHawk.Client.Common
{
public static class ConfigService
{
internal static readonly JsonSerializer Serializer;

static ConfigService()
private static readonly JsonSerializerOptions NonIndentedSerializerOptions = new()
{
Serializer = new JsonSerializer
IncludeFields = true,
AllowTrailingCommas = true,
Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping,
Converters =
{
MissingMemberHandling = MissingMemberHandling.Ignore,
TypeNameHandling = TypeNameHandling.Auto,
ConstructorHandling = ConstructorHandling.Default,

// because of the peculiar setup of Binding.cs and PathEntry.cs
ObjectCreationHandling = ObjectCreationHandling.Replace,

ContractResolver = new DefaultContractResolver
{
DefaultMembersSearchFlags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.NonPublic
},
};
}
new FloatConverter(), // this serializes floats with minimum required precision, e.g. 1.8000000012 -> 1.8
new ByteArrayAsNormalArrayJsonConverter(), // this preserves the old behaviour of e,g, 0x1234ABCD --> [18,52,171,205]; omitting it will use base64 e.g. "EjSrzQ=="
new TypeConverterJsonAdapterFactory(), // allows serialization using `[TypeConverter]` attributes
}
};

private static readonly JsonSerializerOptions IndentedSerializerOptions = new(NonIndentedSerializerOptions)
{
WriteIndented = true
};

public static JsonSerializerOptions SerializerOptions => NonIndentedSerializerOptions;

public static bool IsFromSameVersion(string filepath, out string msg)
{
Expand All @@ -48,7 +45,7 @@ public static bool IsFromSameVersion(string filepath, out string msg)
string cfgVersionStr = null;
try
{
cfgVersionStr = JObject.Parse(File.ReadAllText(filepath))["LastWrittenFrom"]?.Value<string>();
cfgVersionStr = JsonNode.Parse(File.ReadAllText(filepath))["LastWrittenFrom"]?.GetValue<string>();
}
catch (Exception)
{
Expand Down Expand Up @@ -84,16 +81,15 @@ public static bool IsFromSameVersion(string filepath, out string msg)
/// <exception cref="InvalidOperationException">internal error</exception>
public static T Load<T>(string filepath) where T : new()
{
T config = default(T);
T config = default;

try
{
var file = new FileInfo(filepath);
if (file.Exists)
{
using var reader = file.OpenText();
var r = new JsonTextReader(reader);
config = (T)Serializer.Deserialize(r, typeof(T));
using var reader = file.OpenRead();
config = JsonSerializer.Deserialize<T>(reader, IndentedSerializerOptions);
}
}
catch (Exception ex)
Expand All @@ -106,12 +102,10 @@ public static bool IsFromSameVersion(string filepath, out string msg)

public static void Save(string filepath, object config)
{
var file = new FileInfo(filepath);
try
{
using var writer = file.CreateText();
var w = new JsonTextWriter(writer) { Formatting = Formatting.Indented };
Serializer.Serialize(w, config);
using var writer = File.Create(filepath);
JsonSerializer.Serialize(writer, config, IndentedSerializerOptions);
}
catch
{
Expand All @@ -125,25 +119,30 @@ private class TypeNameEncapsulator
public object o;
}

public static object LoadWithType(string serialized)
public static T LoadWithType<T>(string serialized)
{
using var tr = new StringReader(serialized);
using var jr = new JsonTextReader(tr);
var tne = (TypeNameEncapsulator)Serializer.Deserialize(jr, typeof(TypeNameEncapsulator));
var tne = JsonSerializer.Deserialize<TypeNameEncapsulator>(serialized, SerializerOptions);

if (tne?.o is JsonElement jsonElement)
return jsonElement.Deserialize<T>(SerializerOptions);

return default;
}

public static object LoadWithType(string serialized, Type deserializedType)
{
var tne = JsonSerializer.Deserialize<TypeNameEncapsulator>(serialized, SerializerOptions);

if (tne?.o is JsonElement jsonElement)
return jsonElement.Deserialize(deserializedType, SerializerOptions);

// in the case of trying to deserialize nothing, tne will be nothing
// we want to return nothing
return tne?.o;
return null;
}

public static string SaveWithType(object o)
{
using var sw = new StringWriter();
using var jw = new JsonTextWriter(sw) { Formatting = Formatting.None };
var tne = new TypeNameEncapsulator { o = o };
Serializer.Serialize(jw, tne, typeof(TypeNameEncapsulator));
sw.Flush();
return sw.ToString();
return JsonSerializer.Serialize(tne, SerializerOptions);
}
}
}
2 changes: 1 addition & 1 deletion src/BizHawk.Client.Common/config/FeedbackBind.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#nullable enable

using Newtonsoft.Json;
using System.Text.Json.Serialization;

namespace BizHawk.Client.Common
{
Expand Down
3 changes: 1 addition & 2 deletions src/BizHawk.Client.Common/config/PathEntry.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
using Newtonsoft.Json;
using System.Text.Json.Serialization;

namespace BizHawk.Client.Common
{
public sealed class PathEntry
{
public string Type { get; set; }
[JsonIgnore]
private string _path;
public string Path
{
Expand Down
4 changes: 1 addition & 3 deletions src/BizHawk.Client.Common/config/PathEntryCollection.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json.Serialization;

using BizHawk.Common.CollectionExtensions;
using BizHawk.Common.PathExtensions;
using BizHawk.Emulation.Common;

using Newtonsoft.Json;

namespace BizHawk.Client.Common
{
public class PathEntryCollection
Expand Down Expand Up @@ -164,7 +163,6 @@ public void ResolveWithDefaults()
[JsonIgnore]
public string FirmwaresPathFragment => this[GLOBAL, "Firmware"].Path;

[JsonIgnore]
internal string TempFilesFragment => this[GLOBAL, "Temp Files"].Path;

public static readonly Lazy<IReadOnlyList<PathEntry>> Defaults = new(() => new[]
Expand Down
8 changes: 6 additions & 2 deletions src/BizHawk.Client.Common/config/ToolDialogSettings.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
using System.Collections.Generic;
using System.Drawing;
using System.Linq;

using Newtonsoft.Json;
using System.Text.Json.Serialization;

namespace BizHawk.Client.Common
{
public class ToolDialogSettings
{
// one may wonder why the public property is getting JsonIgnored and the private one JsonIncluded
[JsonInclude]
[JsonPropertyOrder(-2)]
private int? _wndx;
[JsonInclude]
[JsonPropertyOrder(-1)]
private int? _wndy;

public ToolDialogSettings()
Expand Down
8 changes: 2 additions & 6 deletions src/BizHawk.Client.Common/lua/LuaDocumentation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
using System.Linq;
using System.Reflection;
using System.Text;
using Newtonsoft.Json;
using System.Text.Json;

namespace BizHawk.Client.Common
{
Expand Down Expand Up @@ -117,18 +117,14 @@ public SublimeCompletions()
Scope = "source.lua - string";
}

[JsonProperty(PropertyName = "scope")]
public string Scope { get; set; }

[JsonProperty(PropertyName = "completions")]
public List<Completion> Completions { get; set; } = new List<Completion>();

public class Completion
{
[JsonProperty(PropertyName = "trigger")]
public string Trigger { get; set; }

[JsonProperty(PropertyName = "contents")]
public string Contents { get; set; }
}
}
Expand Down Expand Up @@ -184,7 +180,7 @@ public string ToSublime2CompletionList()
sc.Completions.Add(completion);
}

return JsonConvert.SerializeObject(sc);
return JsonSerializer.Serialize(sc, new JsonSerializerOptions {PropertyNamingPolicy = JsonNamingPolicy.CamelCase});
}

public string ToNotepadPlusPlusAutoComplete()
Expand Down
Loading