From 387a8203928a261ee7c82026e993ba193fdbf44e Mon Sep 17 00:00:00 2001 From: karashiiro <49822414+karashiiro@users.noreply.github.com> Date: Fri, 13 May 2022 13:07:51 -0700 Subject: [PATCH] Fix SeString serialization/deserialization (#840) * chore: add SeString JSON conversion test * fix: SeString JSON serialization * chore: add SeString JSON deserialization test * chore: rename tests to abstract format away * chore: fix typos * fix: deserializable SeStrings * chore: add comment to test * chore: use DeserializeConfig in Load --- .../Text/SeStringHandling/SeStringTests.cs | 68 +++++++++++++++++++ Dalamud/Configuration/PluginConfigurations.cs | 44 ++++++++---- Dalamud/Game/Text/SeStringHandling/Payload.cs | 6 +- 3 files changed, 104 insertions(+), 14 deletions(-) create mode 100644 Dalamud.Test/Game/Text/SeStringHandling/SeStringTests.cs diff --git a/Dalamud.Test/Game/Text/SeStringHandling/SeStringTests.cs b/Dalamud.Test/Game/Text/SeStringHandling/SeStringTests.cs new file mode 100644 index 000000000..9a48a6615 --- /dev/null +++ b/Dalamud.Test/Game/Text/SeStringHandling/SeStringTests.cs @@ -0,0 +1,68 @@ +using System; +using Dalamud.Configuration; +using Dalamud.Game.Text.SeStringHandling; +using Xunit; + +namespace Dalamud.Test.Game.Text.SeStringHandling +{ + public class SeStringTests + { + private class MockConfig : IPluginConfiguration, IEquatable + { + public int Version { get; set; } + + public SeString Text { get; init; } + + public bool Equals(MockConfig other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Version == other.Version && Equals(Text.TextValue, other.Text.TextValue); + } + + public override bool Equals(object obj) + { + return Equals(obj as MockConfig); + } + + public override int GetHashCode() + { + return HashCode.Combine(Text); + } + + public static bool operator ==(MockConfig left, MockConfig right) + { + return Equals(left, right); + } + + public static bool operator !=(MockConfig left, MockConfig right) + { + return !Equals(left, right); + } + } + + // Dalamud#779 + [Fact] + public void TestConfigSerializable() + { + var builder = new SeStringBuilder(); + var seString = builder.AddText("Some text").Build(); + var config = new MockConfig { Text = seString }; + PluginConfigurations.SerializeConfig(config); + } + + [Fact] + public void TestConfigDeserializable() + { + var builder = new SeStringBuilder(); + var seString = builder.AddText("Some text").Build(); + var config = new MockConfig { Text = seString }; + + // This relies on the type information being maintained, which is why we're using these + // static methods instead of default serialization/deserialization. + var configSerialized = PluginConfigurations.SerializeConfig(config); + var configDeserialized = (MockConfig)PluginConfigurations.DeserializeConfig(configSerialized); + Assert.Equal(config, configDeserialized); + } + } +} diff --git a/Dalamud/Configuration/PluginConfigurations.cs b/Dalamud/Configuration/PluginConfigurations.cs index faa01af99..c3b5c0d60 100644 --- a/Dalamud/Configuration/PluginConfigurations.cs +++ b/Dalamud/Configuration/PluginConfigurations.cs @@ -21,6 +21,36 @@ namespace Dalamud.Configuration this.configDirectory.Create(); } + /// + /// Serializes a plugin configuration object. + /// + /// The configuration object. + /// A string representing the serialized configuration object. + internal static string SerializeConfig(object? config) + { + return JsonConvert.SerializeObject(config, Formatting.Indented, new JsonSerializerSettings + { + TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple, + TypeNameHandling = TypeNameHandling.Objects, + }); + } + + /// + /// Deserializes a plugin configuration from a string. + /// + /// The serialized configuration. + /// The configuration object, or null. + internal static IPluginConfiguration? DeserializeConfig(string data) + { + return JsonConvert.DeserializeObject( + data, + new JsonSerializerSettings + { + TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple, + TypeNameHandling = TypeNameHandling.Objects, + }); + } + /// /// Save/Load plugin configuration. /// NOTE: Save/Load are still using Type information for now, @@ -32,11 +62,7 @@ namespace Dalamud.Configuration /// Plugin name. public void Save(IPluginConfiguration config, string pluginName) { - File.WriteAllText(this.GetConfigFile(pluginName).FullName, JsonConvert.SerializeObject(config, Formatting.Indented, new JsonSerializerSettings - { - TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple, - TypeNameHandling = TypeNameHandling.Objects, - })); + File.WriteAllText(this.GetConfigFile(pluginName).FullName, SerializeConfig(config)); } /// @@ -51,13 +77,7 @@ namespace Dalamud.Configuration if (!path.Exists) return null; - return JsonConvert.DeserializeObject( - File.ReadAllText(path.FullName), - new JsonSerializerSettings - { - TypeNameAssemblyFormatHandling = TypeNameAssemblyFormatHandling.Simple, - TypeNameHandling = TypeNameHandling.Objects, - }); + return DeserializeConfig(File.ReadAllText(path.FullName)); } /// diff --git a/Dalamud/Game/Text/SeStringHandling/Payload.cs b/Dalamud/Game/Text/SeStringHandling/Payload.cs index 8a1e03cb1..9f8b4e9ff 100644 --- a/Dalamud/Game/Text/SeStringHandling/Payload.cs +++ b/Dalamud/Game/Text/SeStringHandling/Payload.cs @@ -5,6 +5,7 @@ using System.IO; using Dalamud.Data; using Dalamud.Game.Text.SeStringHandling.Payloads; +using Newtonsoft.Json; using Serilog; // TODOs: @@ -29,6 +30,7 @@ namespace Dalamud.Game.Text.SeStringHandling /// /// Gets the Lumina instance to use for any necessary data lookups. /// + [JsonIgnore] public DataManager DataResolver => Service.Get(); /// @@ -231,13 +233,13 @@ namespace Dalamud.Game.Text.SeStringHandling /// /// The start byte of a payload. /// - [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "This is prefered.")] + [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "This is preferred.")] protected const byte START_BYTE = 0x02; /// /// The end byte of a payload. /// - [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "This is prefered.")] + [SuppressMessage("StyleCop.CSharp.NamingRules", "SA1310:Field names should not contain underscore", Justification = "This is preferred.")] protected const byte END_BYTE = 0x03; ///