From 708b2ec361054b6e82bcd61007aaf7a473c91855 Mon Sep 17 00:00:00 2001 From: Alex Date: Wed, 8 Jan 2025 22:37:10 +0000 Subject: [PATCH] more serialization and other fixes --- Core/Models/Eft/Common/Tables/BotBase.cs | 10 +++- Core/Utils/ImporterUtil.cs | 15 ++++- .../DictionaryOfListOrTConverter.cs | 43 +++++++++++++++ .../Utils/Json/Converters/ListOrTConverter.cs | 55 +++++++++++++++++++ Core/Utils/Json/ListOrT.cs | 10 ++++ 5 files changed, 127 insertions(+), 6 deletions(-) create mode 100644 Core/Utils/Json/Converters/DictionaryOfListOrTConverter.cs create mode 100644 Core/Utils/Json/Converters/ListOrTConverter.cs create mode 100644 Core/Utils/Json/ListOrT.cs diff --git a/Core/Models/Eft/Common/Tables/BotBase.cs b/Core/Models/Eft/Common/Tables/BotBase.cs index 46fc153b..6d2789cd 100644 --- a/Core/Models/Eft/Common/Tables/BotBase.cs +++ b/Core/Models/Eft/Common/Tables/BotBase.cs @@ -2,6 +2,7 @@ using System.Text.Json.Serialization; using Core.Models.Eft.Ragfair; using Core.Models.Enums; +using Core.Utils.Json; using Core.Utils.Json.Converters; namespace Core.Models.Eft.Common.Tables; @@ -293,6 +294,9 @@ public class BotBaseInventory [JsonPropertyName("favoriteItems")] public List? FavoriteItems { get; set; } + + [JsonPropertyName("hideoutCustomizationStashId")] + public string? HideoutCustomizationStashId { get; set; } } public class BaseJsonSkills @@ -304,8 +308,8 @@ public class BaseJsonSkills public class Skills { - [JsonConverter(typeof(ArrayToObjectFactoryConverter))] - public Dictionary? Common { get; set; } + [JsonConverter(typeof(DictionaryOfListOrTConverter))] + public Dictionary>? Common { get; set; } [JsonConverter(typeof(ArrayToObjectFactoryConverter))] public Dictionary? Mastering { get; set; } @@ -681,7 +685,7 @@ public class LastCompleted public class Notes { - [JsonPropertyName("notes")] + [JsonPropertyName("Notes")] public List? DataNotes { get; set; } } diff --git a/Core/Utils/ImporterUtil.cs b/Core/Utils/ImporterUtil.cs index 35291092..80bdf70b 100644 --- a/Core/Utils/ImporterUtil.cs +++ b/Core/Utils/ImporterUtil.cs @@ -2,6 +2,7 @@ using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; using Core.Annotations; +using Core.Utils.Json.Converters; namespace Core.Utils; @@ -12,6 +13,11 @@ public class ImporterUtil private readonly HashSet filesToIgnore = ["bearsuits.json", "usecsuits.json", "archivedquests.json"]; + private readonly JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions + { + UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow, Converters = { new ListOrTConverterFactory() } + }; + public ImporterUtil(FileUtil fileUtil) { _fileUtil = fileUtil; @@ -58,8 +64,7 @@ public class ImporterUtil ); try { - var fileDeserialized = JsonSerializer.Deserialize(fileData, propertyType, - new JsonSerializerOptions { UnmappedMemberHandling = JsonUnmappedMemberHandling.Disallow }); + var fileDeserialized = JsonSerializer.Deserialize(fileData, propertyType, jsonSerializerOptions); if (onObjectDeserialized != null) onObjectDeserialized(file, fileDeserialized); @@ -77,13 +82,17 @@ public class ImporterUtil // deep tree search foreach (var directory in directories) { + var dictionaryLock = new object(); tasks.Add( Task.Factory.StartNew(() => { var setMethod = GetSetMethod(directory.Split("/").Last().Replace("_", ""), loadedType, out var matchedProperty, out var isDictionary); var loadTask = LoadRecursiveAsync($"{directory}/", matchedProperty); loadTask.Wait(); - setMethod.Invoke(result, isDictionary ? [directory, loadTask.Result] : [loadTask.Result]); + lock (dictionaryLock) + { + setMethod.Invoke(result, isDictionary ? [directory, loadTask.Result] : [loadTask.Result]); + } }) ); } diff --git a/Core/Utils/Json/Converters/DictionaryOfListOrTConverter.cs b/Core/Utils/Json/Converters/DictionaryOfListOrTConverter.cs new file mode 100644 index 00000000..aeb0c695 --- /dev/null +++ b/Core/Utils/Json/Converters/DictionaryOfListOrTConverter.cs @@ -0,0 +1,43 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Core.Utils.Json.Converters; + +public class DictionaryOfListOrTConverter : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() == typeof(Dictionary<,>) && + typeToConvert.GenericTypeArguments[1].IsGenericType && typeToConvert.GenericTypeArguments[1].GetGenericTypeDefinition() == typeof(ListOrT<>); + } + + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + return (JsonConverter)Activator.CreateInstance(typeof(DictionaryOfListOrTConverter<,>).MakeGenericType(typeToConvert.GenericTypeArguments[0], typeToConvert.GenericTypeArguments[1].GenericTypeArguments[0])); + } +} + +public class DictionaryOfListOrTConverter : JsonConverter>?> +{ + public override Dictionary>? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + if (reader.TokenType == JsonTokenType.StartArray) + { + reader.Read(); + return default; + } + else + { + using (var jsonDocument = JsonDocument.ParseValue(ref reader)) + { + var jsonText = jsonDocument.RootElement.GetRawText(); + return JsonSerializer.Deserialize>>(jsonText, options); + } + } + } + + public override void Write(Utf8JsonWriter writer, Dictionary> value, JsonSerializerOptions options) + { + JsonSerializer.Serialize(writer, value, options); + } +} diff --git a/Core/Utils/Json/Converters/ListOrTConverter.cs b/Core/Utils/Json/Converters/ListOrTConverter.cs new file mode 100644 index 00000000..fca1deea --- /dev/null +++ b/Core/Utils/Json/Converters/ListOrTConverter.cs @@ -0,0 +1,55 @@ +using System.Text.Json; +using System.Text.Json.Serialization; + +namespace Core.Utils.Json.Converters; + +public class ListOrTConverterFactory : JsonConverterFactory +{ + public override bool CanConvert(Type typeToConvert) + { + return typeToConvert.IsGenericType && typeToConvert.GetGenericTypeDefinition() == typeof(ListOrT<>); + } + + public override JsonConverter? CreateConverter(Type typeToConvert, JsonSerializerOptions options) + { + return (JsonConverter)Activator.CreateInstance(typeof(ListOrTConverter<>).MakeGenericType(typeToConvert.GenericTypeArguments[0])); + } +} + +public class ListOrTConverter : JsonConverter?> +{ + public override ListOrT? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + switch (reader.TokenType) + { + case JsonTokenType.StartArray: + using (var jsonDocument = JsonDocument.ParseValue(ref reader)) + { + var jsonText = jsonDocument.RootElement.GetRawText(); + var list = JsonSerializer.Deserialize>(jsonText, options); + return new ListOrT(list, default); + } + case JsonTokenType.StartObject: + using (var jsonDocument = JsonDocument.ParseValue(ref reader)) + { + var jsonText = jsonDocument.RootElement.GetRawText(); + var obj = JsonSerializer.Deserialize(jsonText, options); + return new ListOrT(null, obj); + } + default: + throw new Exception($"Unable to translate object type {reader.TokenType} to ListOrT."); + } + } + + public override void Write(Utf8JsonWriter writer, ListOrT value, JsonSerializerOptions options) + { + if (value.IsItem) + { + JsonSerializer.Serialize(writer, value.Item, options); + } + else + { + JsonSerializer.Serialize(writer, value.List, options); + } + } +} diff --git a/Core/Utils/Json/ListOrT.cs b/Core/Utils/Json/ListOrT.cs new file mode 100644 index 00000000..bfa0aae4 --- /dev/null +++ b/Core/Utils/Json/ListOrT.cs @@ -0,0 +1,10 @@ +namespace Core.Utils.Json; + +public class ListOrT(List? list, T? item) +{ + public List? List { get; } = list; + public T? Item { get; } = item; + + public bool IsItem => Item != null; + public bool IsList => List != null; +}