From f6d439f92ce9a633a5ce380e6c7f5e0ef63fbe90 Mon Sep 17 00:00:00 2001 From: Paul Hebble Date: Sun, 20 Aug 2023 15:05:32 -0500 Subject: [PATCH] Rename module_identifiers to module_identifiers_by_game Handle fields and nested converters in prop name changed converter --- .../JsonPropertyNamesChangedConverter.cs | 39 ++++++++++++++++--- GUI/Labels/ModuleLabel.cs | 15 ++++++- 2 files changed, 48 insertions(+), 6 deletions(-) diff --git a/Core/Converters/JsonPropertyNamesChangedConverter.cs b/Core/Converters/JsonPropertyNamesChangedConverter.cs index e5769b179e..a9e125567b 100644 --- a/Core/Converters/JsonPropertyNamesChangedConverter.cs +++ b/Core/Converters/JsonPropertyNamesChangedConverter.cs @@ -49,7 +49,6 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { object instance = Activator.CreateInstance(objectType); - var props = objectType.GetTypeInfo().DeclaredProperties.ToList(); JObject jo = JObject.Load(reader); var changes = mapping; foreach (JProperty jp in jo.Properties()) @@ -59,14 +58,44 @@ public override object ReadJson(JsonReader reader, Type objectType, object exist { name = jp.Name; } - PropertyInfo prop = props.FirstOrDefault(pi => pi.CanWrite && ( - pi.GetCustomAttribute()?.PropertyName == name - || pi.Name == name)); - prop?.SetValue(instance, jp.Value.ToObject(prop.PropertyType, serializer)); + PropertyInfo prop = objectType.GetTypeInfo().DeclaredProperties.FirstOrDefault(pi => + pi.CanWrite + && (pi.GetCustomAttribute()?.PropertyName ?? pi.Name) == name); + if (prop != null) + { + prop.SetValue(instance, + GetValue(prop.GetCustomAttribute(), + jp.Value, prop.PropertyType, serializer)); + } + else + { + // No property, maybe there's a field + FieldInfo field = objectType.GetTypeInfo().DeclaredFields.FirstOrDefault(fi => + (fi.GetCustomAttribute()?.PropertyName ?? fi.Name) == name); + if (field != null) + { + field.SetValue(instance, + GetValue(field.GetCustomAttribute(), + jp.Value, field.FieldType, serializer)); + } + } } return instance; } + private static object GetValue(JsonConverterAttribute attrib, + JToken value, Type outputType, JsonSerializer serializer) + => attrib != null ? ApplyConverter((JsonConverter)Activator.CreateInstance(attrib.ConverterType, + attrib.ConverterParameters), + value, outputType, serializer) + : value.ToObject(outputType, serializer); + + private static object ApplyConverter(JsonConverter converter, + JToken value, Type outputType, JsonSerializer serializer) + => converter.CanRead ? converter.ReadJson(new JTokenReader(value), + outputType, null, serializer) + : value.ToObject(outputType, serializer); + /// /// This is what you need to override in your child class /// diff --git a/GUI/Labels/ModuleLabel.cs b/GUI/Labels/ModuleLabel.cs index 8587e385d6..6858a69113 100644 --- a/GUI/Labels/ModuleLabel.cs +++ b/GUI/Labels/ModuleLabel.cs @@ -6,6 +6,7 @@ namespace CKAN.GUI { [JsonObject(MemberSerialization.OptIn)] + [JsonConverter(typeof(ModuleIdentifiersRenamedConverter))] public class ModuleLabel { [JsonProperty("name", NullValueHandling = NullValueHandling.Ignore)] @@ -41,7 +42,7 @@ public class ModuleLabel [DefaultValue(false)] public bool HoldVersion; - [JsonProperty("module_identifiers", NullValueHandling = NullValueHandling.Ignore)] + [JsonProperty("module_identifiers_by_game", NullValueHandling = NullValueHandling.Ignore)] public HashSet ModuleIdentifiers = new HashSet(); /// @@ -72,4 +73,16 @@ public void Remove(string identifier) ModuleIdentifiers.Remove(identifier); } } + + /// + /// Protect old clients from trying to load a file they can't parse + /// + public class ModuleIdentifiersRenamedConverter : JsonPropertyNamesChangedConverter + { + protected override Dictionary mapping + => new Dictionary + { + { "module_identifiers", "module_identifiers_by_game" } + }; + } }