From 518e892c721564f68850a6a0a68a80536a1d249b Mon Sep 17 00:00:00 2001 From: Steven Liekens Date: Sat, 25 Jan 2025 19:46:14 +0100 Subject: [PATCH] Add json converters for recipes --- .../Features/Hero/Crafting/Recipes/Recipes.cs | 23 +- GW2SDK/.filenesting.json | 3 + .../Features/Hero/Crafting/GuildIngredient.cs | 5 +- .../Crafting/GuildIngredientJsonConverter.cs | 36 ++ .../Hero/Crafting/IngredientJsonConverter.cs | 38 ++ .../Hero/Crafting/Recipes/AmuletRecipe.cs | 5 +- .../Recipes/AmuletRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/AxeRecipe.cs | 5 +- .../Recipes/AxeRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/BackpackRecipe.cs | 5 +- .../Recipes/BackpackRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/BagRecipe.cs | 5 +- .../Recipes/BagRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/BootsRecipe.cs | 7 +- .../Recipes/BootsRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/BulkRecipe.cs | 7 +- .../Recipes/BulkRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/CoatRecipe.cs | 5 +- .../Recipes/CoatRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/ComponentRecipe.cs | 5 +- .../Recipes/ComponentRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/ConsumableRecipe.cs | 5 +- .../Recipes/ConsumableRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/DaggerRecipe.cs | 5 +- .../Recipes/DaggerRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/DessertRecipe.cs | 5 +- .../Recipes/DessertRecipeJsonConverter.cs | 52 +++ .../Hero/Crafting/Recipes/DyeRecipe.cs | 5 +- .../Recipes/DyeRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/EarringRecipe.cs | 5 +- .../Recipes/EarringRecipeJsonConverter.cs | 52 +++ .../Hero/Crafting/Recipes/FeastRecipe.cs | 5 +- .../Recipes/FeastRecipeJsonConverter.cs | 52 +++ .../Hero/Crafting/Recipes/FocusRecipe.cs | 5 +- .../Recipes/FocusRecipeJsonConverter.cs | 52 +++ .../Hero/Crafting/Recipes/GlovesRecipe.cs | 7 +- .../Recipes/GlovesRecipeJsonConverter.cs | 52 +++ .../Hero/Crafting/Recipes/GreatswordRecipe.cs | 5 +- .../Recipes/GreatswordRecipeJsonConverter.cs | 53 +++ .../Crafting/Recipes/GuildConsumableRecipe.cs | 30 +- .../GuildConsumableRecipeJsonConverter.cs | 62 +++ .../Crafting/Recipes/GuildDecorationRecipe.cs | 29 +- .../GuildDecorationRecipeJsonConverter.cs | 63 +++ .../Crafting/Recipes/GuildWvwUpgradeRecipe.cs | 19 +- .../GuildWvwUpgradeRecipeJsonConverter.cs | 62 +++ .../Hero/Crafting/Recipes/HammerRecipe.cs | 5 +- .../Recipes/HammerRecipeJsonConverter.cs | 52 +++ .../Hero/Crafting/Recipes/HarpoonGunRecipe.cs | 5 +- .../Recipes/HarpoonGunRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/HeadgearRecipe.cs | 5 +- .../Recipes/HeadgearRecipeJsonConverter.cs | 52 +++ .../Recipes/IngredientCookingRecipe.cs | 5 +- .../IngredientCookingRecipeJsonConverter.cs | 53 +++ .../Crafting/Recipes/InscriptionRecipe.cs | 7 +- .../Recipes/InscriptionRecipeJsonConverter.cs | 52 +++ .../Hero/Crafting/Recipes/InsigniaRecipe.cs | 7 +- .../Recipes/InsigniaRecipeJsonConverter.cs | 53 +++ .../Recipes/LegendaryComponentRecipe.cs | 5 +- .../LegendaryComponentRecipeJsonConverter.cs | 53 +++ .../Hero/Crafting/Recipes/LeggingsRecipe.cs | 5 +- .../Recipes/LeggingsRecipeJsonConverter.cs | 52 +++ .../Hero/Crafting/Recipes/LongBowRecipe.cs | 5 +- .../Recipes/LongbowRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/MaceRecipe.cs | 5 +- .../Recipes/MaceRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/MealRecipe.cs | 7 +- .../Recipes/MealRecipeJsonConverter.cs | 53 +++ .../Hero/Crafting/Recipes/PistolRecipe.cs | 5 +- .../Recipes/PistolRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/PotionRecipe.cs | 5 +- .../Recipes/PotionRecipeJsonConverter.cs | 51 +++ .../Features/Hero/Crafting/Recipes/Recipe.cs | 44 ++- .../Hero/Crafting/Recipes/RecipeFlags.cs | 7 +- .../Recipes/RecipeFlagsJsonConverter.cs | 51 +++ .../Crafting/Recipes/RecipeJsonConverter.cs | 359 ++++++++++++++++++ .../Recipes/RefinementEctoplasmRecipe.cs | 5 +- .../RefinementEctoplasmRecipeJsonConverter.cs | 53 +++ .../Recipes/RefinementObsidianRecipe.cs | 5 +- .../RefinementObsidianRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/RefinementRecipe.cs | 5 +- .../Recipes/RefinementRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/RifleRecipe.cs | 5 +- .../Recipes/RifleRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/RingRecipe.cs | 5 +- .../Recipes/RingRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/ScepterRecipe.cs | 5 +- .../Recipes/ScepterRecipeJsonConverter.cs | 52 +++ .../Hero/Crafting/Recipes/SeasoningRecipe.cs | 5 +- .../Recipes/SeasoningRecipeJsonConverter.cs | 53 +++ .../Hero/Crafting/Recipes/ShieldRecipe.cs | 5 +- .../Recipes/ShieldRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/ShortBowRecipe.cs | 5 +- .../Recipes/ShortbowRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/ShouldersRecipe.cs | 5 +- .../Recipes/ShouldersRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/SnackRecipe.cs | 7 +- .../Recipes/SnackRecipeJsonConverter.cs | 53 +++ .../Hero/Crafting/Recipes/SoupRecipe.cs | 7 +- .../Recipes/SoupRecipeJsonConverter.cs | 53 +++ .../Hero/Crafting/Recipes/SpearRecipe.cs | 5 +- .../Recipes/SpearRecipeJsonConverter.cs | 52 +++ .../Hero/Crafting/Recipes/StaffRecipe.cs | 5 +- .../Recipes/StaffRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/SwordRecipe.cs | 5 +- .../Recipes/SwordRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/TorchRecipe.cs | 5 +- .../Recipes/TorchRecipeJsonConverter.cs | 52 +++ .../Hero/Crafting/Recipes/TridentRecipe.cs | 5 +- .../Recipes/TridentRecipeJsonConverter.cs | 52 +++ .../Recipes/UpgradeComponentRecipe.cs | 5 +- .../UpgradeComponentRecipeJsonConverter.cs | 51 +++ .../Hero/Crafting/Recipes/WarhornRecipe.cs | 5 +- .../Recipes/WarhornRecipeJsonConverter.cs | 52 +++ 113 files changed, 3559 insertions(+), 64 deletions(-) create mode 100644 GW2SDK/Features/Hero/Crafting/GuildIngredientJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/IngredientJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/AmuletRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/AxeRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/BackpackRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/BagRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/BootsRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/BulkRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/CoatRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/ComponentRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/ConsumableRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/DaggerRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/DessertRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/DyeRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/EarringRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/FeastRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/FocusRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/GlovesRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/GreatswordRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/GuildConsumableRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/GuildDecorationRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/GuildWvwUpgradeRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/HammerRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/HarpoonGunRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/HeadgearRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/IngredientCookingRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/InscriptionRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/InsigniaRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/LegendaryComponentRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/LeggingsRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/LongbowRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/MaceRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/MealRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/PistolRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/PotionRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/RecipeFlagsJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/RecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/RefinementEctoplasmRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/RefinementObsidianRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/RefinementRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/RifleRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/RingRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/ScepterRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/SeasoningRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/ShieldRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/ShortbowRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/ShouldersRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/SnackRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/SoupRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/SpearRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/StaffRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/SwordRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/TorchRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/TridentRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/UpgradeComponentRecipeJsonConverter.cs create mode 100644 GW2SDK/Features/Hero/Crafting/Recipes/WarhornRecipeJsonConverter.cs diff --git a/GW2SDK.Tests/Features/Hero/Crafting/Recipes/Recipes.cs b/GW2SDK.Tests/Features/Hero/Crafting/Recipes/Recipes.cs index 06b476631..51e4da97f 100644 --- a/GW2SDK.Tests/Features/Hero/Crafting/Recipes/Recipes.cs +++ b/GW2SDK.Tests/Features/Hero/Crafting/Recipes/Recipes.cs @@ -1,4 +1,6 @@ -using GuildWars2.Chat; +using System.Text.Json; +using GuildWars2.Chat; +using GuildWars2.Hero.Crafting.Recipes; using GuildWars2.Tests.TestInfrastructure; namespace GuildWars2.Tests.Features.Hero.Crafting.Recipes; @@ -44,4 +46,23 @@ public async Task Can_be_enumerated() Assert.Equal(chatLink.ToString(), chatLinkRoundtrip.ToString()); } } + + [Fact] + public async Task Can_be_serialized() + { + // The JsonLinesHttpMessageHandler simulates the behavior of the real API + // because bulk enumeration quickly exhausts the API rate limit + using var httpClient = + new HttpClient(new JsonLinesHttpMessageHandler("Data/recipes.jsonl.gz")); + var sut = new Gw2Client(httpClient); + await foreach (var original in sut.Hero.Crafting.Recipes + .GetRecipesBulk(cancellationToken: TestContext.Current.CancellationToken) + .ValueOnly(TestContext.Current.CancellationToken)) + { + var json = JsonSerializer.Serialize(original); + var roundTrip = JsonSerializer.Deserialize(json); + Assert.IsType(original.GetType(), roundTrip); + Assert.Equal(original, roundTrip); + } + } } diff --git a/GW2SDK/.filenesting.json b/GW2SDK/.filenesting.json index 8500d068e..ee0a6e556 100644 --- a/GW2SDK/.filenesting.json +++ b/GW2SDK/.filenesting.json @@ -6,6 +6,9 @@ "add": { "Json.cs": [ ".cs" + ], + "JsonConverter.cs": [ + ".cs" ] } } diff --git a/GW2SDK/Features/Hero/Crafting/GuildIngredient.cs b/GW2SDK/Features/Hero/Crafting/GuildIngredient.cs index f243685dd..faa528139 100644 --- a/GW2SDK/Features/Hero/Crafting/GuildIngredient.cs +++ b/GW2SDK/Features/Hero/Crafting/GuildIngredient.cs @@ -1,7 +1,10 @@ -namespace GuildWars2.Hero.Crafting; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting; /// Information about a guild consumable or decoration ingredient required to craft a guild upgrade. [PublicAPI] +[JsonConverter(typeof(GuildIngredientJsonConverter))] [DataTransferObject] public sealed record GuildIngredient { diff --git a/GW2SDK/Features/Hero/Crafting/GuildIngredientJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/GuildIngredientJsonConverter.cs new file mode 100644 index 000000000..fa4e36ff6 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/GuildIngredientJsonConverter.cs @@ -0,0 +1,36 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting; + +internal sealed class GuildIngredientJsonConverter : JsonConverter +{ + public override GuildIngredient Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, GuildIngredient value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static GuildIngredient Read(JsonElement json) + { + return new GuildIngredient + { + UpgradeId = json.GetProperty("upgrade_id").GetInt32(), + Count = json.GetProperty("count").GetInt32() + }; + } + + public static void Write(Utf8JsonWriter writer, GuildIngredient value) + { + writer.WriteStartObject(); + writer.WriteNumber("upgrade_id", value.UpgradeId); + writer.WriteNumber("count", value.Count); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/IngredientJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/IngredientJsonConverter.cs new file mode 100644 index 000000000..2180e5db8 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/IngredientJsonConverter.cs @@ -0,0 +1,38 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting; + +internal sealed class IngredientJsonConverter : JsonConverter +{ + public override Ingredient Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, Ingredient value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static Ingredient Read(JsonElement json) + { + return new Ingredient + { + Kind = json.GetProperty("kind").GetEnum(), + Id = json.GetProperty("id").GetInt32(), + Count = json.GetProperty("count").GetInt32() + }; + } + + public static void Write(Utf8JsonWriter writer, Ingredient value) + { + writer.WriteStartObject(); + writer.WriteString("kind", value.Kind.ToString()); + writer.WriteNumber("id", value.Id); + writer.WriteNumber("count", value.Count); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/AmuletRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/AmuletRecipe.cs index 706fc2e2d..b30a95a76 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/AmuletRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/AmuletRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting an amulet. [PublicAPI] +[JsonConverter(typeof(AmuletRecipeJsonConverter))] public sealed record AmuletRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/AmuletRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/AmuletRecipeJsonConverter.cs new file mode 100644 index 000000000..ba9fb6410 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/AmuletRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class AmuletRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "amulet_recipe"; + + public override AmuletRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, AmuletRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static AmuletRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new AmuletRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, AmuletRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/AxeRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/AxeRecipe.cs index a62e2e610..ac28b95ff 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/AxeRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/AxeRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting an axe. [PublicAPI] +[JsonConverter(typeof(AxeRecipeJsonConverter))] public sealed record AxeRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/AxeRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/AxeRecipeJsonConverter.cs new file mode 100644 index 000000000..3c2acd6c7 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/AxeRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class AxeRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "axe_recipe"; + + public override AxeRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, AxeRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static AxeRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new AxeRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, AxeRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/BackpackRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/BackpackRecipe.cs index ba377d033..68ff059ea 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/BackpackRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/BackpackRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a backpack. [PublicAPI] +[JsonConverter(typeof(BackpackRecipeJsonConverter))] public sealed record BackpackRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/BackpackRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/BackpackRecipeJsonConverter.cs new file mode 100644 index 000000000..78bd35c9a --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/BackpackRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class BackpackRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "backpack_recipe"; + + public override BackpackRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, BackpackRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static BackpackRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new BackpackRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, BackpackRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/BagRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/BagRecipe.cs index 747cbd5e9..9e43dd204 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/BagRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/BagRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a bag. [PublicAPI] +[JsonConverter(typeof(BagRecipeJsonConverter))] public sealed record BagRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/BagRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/BagRecipeJsonConverter.cs new file mode 100644 index 000000000..1e22a0bc1 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/BagRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class BagRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "bag_recipe"; + + public override BagRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, BagRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static BagRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new BagRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, BagRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/BootsRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/BootsRecipe.cs index 269b01514..04c69665f 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/BootsRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/BootsRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; -/// Information about a recipe for crafting a pair of boots. +namespace GuildWars2.Hero.Crafting.Recipes; + +/// Information about a recipe for crafting boots. [PublicAPI] +[JsonConverter(typeof(BootsRecipeJsonConverter))] public sealed record BootsRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/BootsRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/BootsRecipeJsonConverter.cs new file mode 100644 index 000000000..23af02282 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/BootsRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class BootsRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "boots_recipe"; + + public override BootsRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, BootsRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static BootsRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new BootsRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, BootsRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/BulkRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/BulkRecipe.cs index 4af6db218..14400874e 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/BulkRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/BulkRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; -/// Information about a recipe for crafting a bulk item, for example armor in bulk or scribing supplies. +namespace GuildWars2.Hero.Crafting.Recipes; + +/// Information about a recipe for bulk crafting. [PublicAPI] +[JsonConverter(typeof(BulkRecipeJsonConverter))] public sealed record BulkRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/BulkRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/BulkRecipeJsonConverter.cs new file mode 100644 index 000000000..634005ee5 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/BulkRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class BulkRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "bulk_recipe"; + + public override BulkRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, BulkRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static BulkRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new BulkRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, BulkRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/CoatRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/CoatRecipe.cs index c8d6bbd16..002cada9d 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/CoatRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/CoatRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a coat. [PublicAPI] +[JsonConverter(typeof(CoatRecipeJsonConverter))] public sealed record CoatRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/CoatRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/CoatRecipeJsonConverter.cs new file mode 100644 index 000000000..3c8d96e88 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/CoatRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class CoatRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "coat_recipe"; + + public override CoatRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, CoatRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static CoatRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new CoatRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, CoatRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/ComponentRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/ComponentRecipe.cs index ba7f028e2..220aeba49 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/ComponentRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/ComponentRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a crafting component, for example a Vision Crystal. [PublicAPI] +[JsonConverter(typeof(ComponentRecipeJsonConverter))] public sealed record ComponentRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/ComponentRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/ComponentRecipeJsonConverter.cs new file mode 100644 index 000000000..405d5720a --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/ComponentRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class ComponentRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "component_recipe"; + + public override ComponentRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, ComponentRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static ComponentRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new ComponentRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, ComponentRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/ConsumableRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/ConsumableRecipe.cs index c9544814c..8366cba79 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/ConsumableRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/ConsumableRecipe.cs @@ -1,6 +1,9 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a consumable item, for example sharpening stones, tonics, metabolic /// primers, varietal seed pouches. [PublicAPI] +[JsonConverter(typeof(ConsumableRecipeJsonConverter))] public sealed record ConsumableRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/ConsumableRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/ConsumableRecipeJsonConverter.cs new file mode 100644 index 000000000..81e6a41e1 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/ConsumableRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class ConsumableRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "consumable_recipe"; + + public override ConsumableRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, ConsumableRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static ConsumableRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new ConsumableRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, ConsumableRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/DaggerRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/DaggerRecipe.cs index 4b5995356..1a4000103 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/DaggerRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/DaggerRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a dagger. [PublicAPI] +[JsonConverter(typeof(DaggerRecipeJsonConverter))] public sealed record DaggerRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/DaggerRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/DaggerRecipeJsonConverter.cs new file mode 100644 index 000000000..f43445979 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/DaggerRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class DaggerRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "dagger_recipe"; + + public override DaggerRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, DaggerRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static DaggerRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new DaggerRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, DaggerRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/DessertRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/DessertRecipe.cs index e60194933..e95cbcdaa 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/DessertRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/DessertRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a dessert, for example Pumpkin Pie. [PublicAPI] +[JsonConverter(typeof(DessertRecipeJsonConverter))] public sealed record DessertRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/DessertRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/DessertRecipeJsonConverter.cs new file mode 100644 index 000000000..d67b2520e --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/DessertRecipeJsonConverter.cs @@ -0,0 +1,52 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class DessertRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "dessert_recipe"; + + public override DessertRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, DessertRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static DessertRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new DessertRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, DessertRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/DyeRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/DyeRecipe.cs index 31f713f4f..5ae93eb65 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/DyeRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/DyeRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a dye. [PublicAPI] +[JsonConverter(typeof(DyeRecipeJsonConverter))] public sealed record DyeRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/DyeRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/DyeRecipeJsonConverter.cs new file mode 100644 index 000000000..3b6ed8338 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/DyeRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class DyeRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "dye_recipe"; + + public override DyeRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, DyeRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static DyeRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new DyeRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, DyeRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/EarringRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/EarringRecipe.cs index f144d75e2..bed96fd86 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/EarringRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/EarringRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting an earring. [PublicAPI] +[JsonConverter(typeof(EarringRecipeJsonConverter))] public sealed record EarringRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/EarringRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/EarringRecipeJsonConverter.cs new file mode 100644 index 000000000..8a155c5b6 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/EarringRecipeJsonConverter.cs @@ -0,0 +1,52 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class EarringRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "earring_recipe"; + + public override EarringRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, EarringRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static EarringRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new EarringRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, EarringRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/FeastRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/FeastRecipe.cs index f88929435..327b94c45 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/FeastRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/FeastRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a feast. [PublicAPI] +[JsonConverter(typeof(FeastRecipeJsonConverter))] public sealed record FeastRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/FeastRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/FeastRecipeJsonConverter.cs new file mode 100644 index 000000000..ee68d4d4d --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/FeastRecipeJsonConverter.cs @@ -0,0 +1,52 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class FeastRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "feast_recipe"; + + public override FeastRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, FeastRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static FeastRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new FeastRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, FeastRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/FocusRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/FocusRecipe.cs index bb623a534..462e8b739 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/FocusRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/FocusRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a focus. [PublicAPI] +[JsonConverter(typeof(FocusRecipeJsonConverter))] public sealed record FocusRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/FocusRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/FocusRecipeJsonConverter.cs new file mode 100644 index 000000000..de9332a4a --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/FocusRecipeJsonConverter.cs @@ -0,0 +1,52 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class FocusRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "focus_recipe"; + + public override FocusRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, FocusRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static FocusRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new FocusRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, FocusRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/GlovesRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/GlovesRecipe.cs index 6ffd4ceba..6840a0f28 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/GlovesRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/GlovesRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; -/// Information about a recipe for crafting a pair of gloves. +namespace GuildWars2.Hero.Crafting.Recipes; + +/// Information about a recipe for crafting gloves. [PublicAPI] +[JsonConverter(typeof(GlovesRecipeJsonConverter))] public sealed record GlovesRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/GlovesRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/GlovesRecipeJsonConverter.cs new file mode 100644 index 000000000..8d46e48d6 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/GlovesRecipeJsonConverter.cs @@ -0,0 +1,52 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class GlovesRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "gloves_recipe"; + + public override GlovesRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, GlovesRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static GlovesRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new GlovesRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, GlovesRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/GreatswordRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/GreatswordRecipe.cs index c026605bd..3a7781e88 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/GreatswordRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/GreatswordRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a greatsword. [PublicAPI] +[JsonConverter(typeof(GreatswordRecipeJsonConverter))] public sealed record GreatswordRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/GreatswordRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/GreatswordRecipeJsonConverter.cs new file mode 100644 index 000000000..6b8947d75 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/GreatswordRecipeJsonConverter.cs @@ -0,0 +1,53 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class GreatswordRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "greatsword_recipe"; + + public override GreatswordRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, GreatswordRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static GreatswordRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new GreatswordRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, GreatswordRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/GuildConsumableRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/GuildConsumableRecipe.cs index 6671e050d..1316779ec 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/GuildConsumableRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/GuildConsumableRecipe.cs @@ -1,7 +1,10 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a guild consumable, for example a banner. [PublicAPI] +[JsonConverter(typeof(GuildConsumableRecipeJsonConverter))] public sealed record GuildConsumableRecipe : Recipe { /// The ingredients from guild storage required to craft the consumable. @@ -9,4 +12,29 @@ public sealed record GuildConsumableRecipe : Recipe /// The guild upgrade ID of the crafted consumable. public required int OutputUpgradeId { get; init; } + + /// + public bool Equals(GuildConsumableRecipe? other) + { + if (other is null) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) + && GuildIngredients.SequenceEqual(other.GuildIngredients) + && OutputUpgradeId == other.OutputUpgradeId; + } + + /// + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(base.GetHashCode()); + foreach (var ingredient in GuildIngredients) + { + hash.Add(ingredient); + } + + hash.Add(OutputUpgradeId); + + return hash.ToHashCode(); + } } diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/GuildConsumableRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/GuildConsumableRecipeJsonConverter.cs new file mode 100644 index 000000000..3c522cbc8 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/GuildConsumableRecipeJsonConverter.cs @@ -0,0 +1,62 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class GuildConsumableRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "guild_consumable_recipe"; + + public override GuildConsumableRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, GuildConsumableRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static GuildConsumableRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new GuildConsumableRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired(), + GuildIngredients = json.GetProperty("guild_ingredients").GetList(GuildIngredientJsonConverter.Read), + OutputUpgradeId = json.GetProperty("output_upgrade_id").GetInt32() + }; + } + + public static void Write(Utf8JsonWriter writer, GuildConsumableRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WritePropertyName("guild_ingredients"); + writer.WriteStartArray(); + foreach (var guildIngredient in value.GuildIngredients) + { + GuildIngredientJsonConverter.Write(writer, guildIngredient); + } + writer.WriteEndArray(); + writer.WriteNumber("output_upgrade_id", value.OutputUpgradeId); + writer.WriteEndObject(); + } +} + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/GuildDecorationRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/GuildDecorationRecipe.cs index 07e784a8d..992f050e5 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/GuildDecorationRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/GuildDecorationRecipe.cs @@ -1,7 +1,10 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a guild decoration. [PublicAPI] +[JsonConverter(typeof(GuildDecorationRecipeJsonConverter))] public sealed record GuildDecorationRecipe : Recipe { /// The ingredients from guild storage required to craft the decoration. @@ -9,4 +12,28 @@ public sealed record GuildDecorationRecipe : Recipe /// The guild upgrade ID of the crafted decoration. public required int OutputUpgradeId { get; init; } + + /// + public bool Equals(GuildDecorationRecipe? other) + { + if (other is null) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) + && GuildIngredients.SequenceEqual(other.GuildIngredients) + && OutputUpgradeId == other.OutputUpgradeId; + } + + /// + public override int GetHashCode() + { + var hash = new HashCode(); + hash.Add(base.GetHashCode()); + foreach (var ingredient in GuildIngredients) + { + hash.Add(ingredient); + } + + hash.Add(OutputUpgradeId); + return hash.ToHashCode(); + } } diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/GuildDecorationRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/GuildDecorationRecipeJsonConverter.cs new file mode 100644 index 000000000..9e53b987b --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/GuildDecorationRecipeJsonConverter.cs @@ -0,0 +1,63 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class GuildDecorationRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "guild_decoration_recipe"; + + public override GuildDecorationRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, GuildDecorationRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static GuildDecorationRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new GuildDecorationRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired(), + GuildIngredients = json.GetProperty("guild_ingredients").GetList(GuildIngredientJsonConverter.Read), + OutputUpgradeId = json.GetProperty("output_upgrade_id").GetInt32() + }; + } + + public static void Write(Utf8JsonWriter writer, GuildDecorationRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WritePropertyName("guild_ingredients"); + writer.WriteStartArray(); + foreach (var guildIngredient in value.GuildIngredients) + { + GuildIngredientJsonConverter.Write(writer, guildIngredient); + } + writer.WriteEndArray(); + + writer.WriteNumber("output_upgrade_id", value.OutputUpgradeId); + writer.WriteEndObject(); + } +} + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/GuildWvwUpgradeRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/GuildWvwUpgradeRecipe.cs index a9b1758b5..c375f9bb6 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/GuildWvwUpgradeRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/GuildWvwUpgradeRecipe.cs @@ -1,10 +1,27 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a guild WvW upgrade: schematics, blueprints, siege weapons. [PublicAPI] +[JsonConverter(typeof(GuildWvwUpgradeRecipeJsonConverter))] public sealed record GuildWvwUpgradeRecipe : Recipe { /// The guild upgrade ID of the crafted upgrade, or null if the recipe is for a component like Vial of /// Enchanted Water. public required int? OutputUpgradeId { get; init; } + + /// + public bool Equals(GuildWvwUpgradeRecipe? other) + { + if (other is null) return false; + if (ReferenceEquals(this, other)) return true; + return base.Equals(other) && OutputUpgradeId == other.OutputUpgradeId; + } + + /// + public override int GetHashCode() + { + return HashCode.Combine(base.GetHashCode(), OutputUpgradeId); + } } diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/GuildWvwUpgradeRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/GuildWvwUpgradeRecipeJsonConverter.cs new file mode 100644 index 000000000..cb310cf6f --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/GuildWvwUpgradeRecipeJsonConverter.cs @@ -0,0 +1,62 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class GuildWvwUpgradeRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "guild_consumable_wvw_recipe"; + + public override GuildWvwUpgradeRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, GuildWvwUpgradeRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static GuildWvwUpgradeRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new GuildWvwUpgradeRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired(), + OutputUpgradeId = json.GetProperty("output_upgrade_id").GetNullableInt32(), + }; + } + + public static void Write(Utf8JsonWriter writer, GuildWvwUpgradeRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + if (value.OutputUpgradeId.HasValue) + { + writer.WriteNumber("output_upgrade_id", value.OutputUpgradeId.Value); + } + else + { + writer.WriteNull("output_upgrade_id"); + } + + writer.WriteEndObject(); + } +} + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/HammerRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/HammerRecipe.cs index 670f9f1f4..d893f6e06 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/HammerRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/HammerRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a hammer. [PublicAPI] +[JsonConverter(typeof(HammerRecipeJsonConverter))] public sealed record HammerRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/HammerRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/HammerRecipeJsonConverter.cs new file mode 100644 index 000000000..5c97326cb --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/HammerRecipeJsonConverter.cs @@ -0,0 +1,52 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class HammerRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "hammer_recipe"; + + public override HammerRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, HammerRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static HammerRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new HammerRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, HammerRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/HarpoonGunRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/HarpoonGunRecipe.cs index 1921cc306..2b75463e3 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/HarpoonGunRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/HarpoonGunRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a harpoon gun. [PublicAPI] +[JsonConverter(typeof(HarpoonGunRecipeJsonConverter))] public sealed record HarpoonGunRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/HarpoonGunRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/HarpoonGunRecipeJsonConverter.cs new file mode 100644 index 000000000..d641f1fb0 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/HarpoonGunRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class HarpoonGunRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "harpoon_gun_recipe"; + + public override HarpoonGunRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, HarpoonGunRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static HarpoonGunRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new HarpoonGunRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, HarpoonGunRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/HeadgearRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/HeadgearRecipe.cs index 58aa9374f..bc2014412 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/HeadgearRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/HeadgearRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting headgear. [PublicAPI] +[JsonConverter(typeof(HeadgearRecipeJsonConverter))] public sealed record HeadgearRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/HeadgearRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/HeadgearRecipeJsonConverter.cs new file mode 100644 index 000000000..7294fb563 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/HeadgearRecipeJsonConverter.cs @@ -0,0 +1,52 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class HeadgearRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "headgear_recipe"; + + public override HeadgearRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, HeadgearRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static HeadgearRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new HeadgearRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, HeadgearRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/IngredientCookingRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/IngredientCookingRecipe.cs index d93c3719d..c1481b7f2 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/IngredientCookingRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/IngredientCookingRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a cooking ingredient, for example a Jar of Tomato Sauce. [PublicAPI] +[JsonConverter(typeof(IngredientCookingRecipeJsonConverter))] public sealed record IngredientCookingRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/IngredientCookingRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/IngredientCookingRecipeJsonConverter.cs new file mode 100644 index 000000000..ac56493b4 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/IngredientCookingRecipeJsonConverter.cs @@ -0,0 +1,53 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class IngredientCookingRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "ingredient_cooking_recipe"; + + public override IngredientCookingRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, IngredientCookingRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static IngredientCookingRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new IngredientCookingRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, IngredientCookingRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/InscriptionRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/InscriptionRecipe.cs index 830cec3d7..39c43b095 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/InscriptionRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/InscriptionRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; -/// Information about a recipe for crafting a weapon inscription. +namespace GuildWars2.Hero.Crafting.Recipes; + +/// Information about a recipe for crafting inscriptions used in weapon crafting. [PublicAPI] +[JsonConverter(typeof(InscriptionRecipeJsonConverter))] public sealed record InscriptionRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/InscriptionRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/InscriptionRecipeJsonConverter.cs new file mode 100644 index 000000000..b9db4cf11 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/InscriptionRecipeJsonConverter.cs @@ -0,0 +1,52 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class InscriptionRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "inscription_recipe"; + + public override InscriptionRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, InscriptionRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static InscriptionRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new InscriptionRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, InscriptionRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/InsigniaRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/InsigniaRecipe.cs index 26477c06a..645dd3afe 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/InsigniaRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/InsigniaRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; -/// Information about a recipe for crafting an armor insignia. +namespace GuildWars2.Hero.Crafting.Recipes; + +/// Information about a recipe for crafting insignias used in armor crafting. [PublicAPI] +[JsonConverter(typeof(InsigniaRecipeJsonConverter))] public sealed record InsigniaRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/InsigniaRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/InsigniaRecipeJsonConverter.cs new file mode 100644 index 000000000..5d61bdb5d --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/InsigniaRecipeJsonConverter.cs @@ -0,0 +1,53 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class InsigniaRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "insignia_recipe"; + + public override InsigniaRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, InsigniaRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static InsigniaRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new InsigniaRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, InsigniaRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/LegendaryComponentRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/LegendaryComponentRecipe.cs index 5be3d6164..653d9e39b 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/LegendaryComponentRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/LegendaryComponentRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a component of a legendary weapon crafting collection. [PublicAPI] +[JsonConverter(typeof(LegendaryComponentRecipeJsonConverter))] public sealed record LegendaryComponentRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/LegendaryComponentRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/LegendaryComponentRecipeJsonConverter.cs new file mode 100644 index 000000000..adea7e065 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/LegendaryComponentRecipeJsonConverter.cs @@ -0,0 +1,53 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class LegendaryComponentRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "legendary_component_recipe"; + + public override LegendaryComponentRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, LegendaryComponentRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static LegendaryComponentRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new LegendaryComponentRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, LegendaryComponentRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/LeggingsRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/LeggingsRecipe.cs index ecb53ac19..a52e57376 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/LeggingsRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/LeggingsRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting leggings. [PublicAPI] +[JsonConverter(typeof(LeggingsRecipeJsonConverter))] public sealed record LeggingsRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/LeggingsRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/LeggingsRecipeJsonConverter.cs new file mode 100644 index 000000000..92cdaea41 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/LeggingsRecipeJsonConverter.cs @@ -0,0 +1,52 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class LeggingsRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "leggings_recipe"; + + public override LeggingsRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, LeggingsRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static LeggingsRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new LeggingsRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, LeggingsRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/LongBowRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/LongBowRecipe.cs index c6f5615c0..a99145453 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/LongBowRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/LongBowRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a longbow. [PublicAPI] +[JsonConverter(typeof(LongbowRecipeJsonConverter))] public sealed record LongbowRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/LongbowRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/LongbowRecipeJsonConverter.cs new file mode 100644 index 000000000..fb4facd37 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/LongbowRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class LongbowRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "longbow_recipe"; + + public override LongbowRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, LongbowRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static LongbowRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new LongbowRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, LongbowRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/MaceRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/MaceRecipe.cs index f23a50dbc..fd587faa9 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/MaceRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/MaceRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a mace. [PublicAPI] +[JsonConverter(typeof(MaceRecipeJsonConverter))] public sealed record MaceRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/MaceRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/MaceRecipeJsonConverter.cs new file mode 100644 index 000000000..f65c4fe3c --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/MaceRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class MaceRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "mace_recipe"; + + public override MaceRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, MaceRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static MaceRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new MaceRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, MaceRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/MealRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/MealRecipe.cs index cc02739f7..3bb02f60c 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/MealRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/MealRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; -/// Information about a recipe for crafting a meal. +namespace GuildWars2.Hero.Crafting.Recipes; + +/// Information about a recipe for cooking meals. [PublicAPI] +[JsonConverter(typeof(MealRecipeJsonConverter))] public sealed record MealRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/MealRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/MealRecipeJsonConverter.cs new file mode 100644 index 000000000..0ed414bed --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/MealRecipeJsonConverter.cs @@ -0,0 +1,53 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class MealRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "meal_recipe"; + + public override MealRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, MealRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static MealRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new MealRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, MealRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/PistolRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/PistolRecipe.cs index 30027209a..141e8dbd5 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/PistolRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/PistolRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a pistol. [PublicAPI] +[JsonConverter(typeof(PistolRecipeJsonConverter))] public sealed record PistolRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/PistolRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/PistolRecipeJsonConverter.cs new file mode 100644 index 000000000..2a52c9a39 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/PistolRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class PistolRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "pistol_recipe"; + + public override PistolRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, PistolRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static PistolRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new PistolRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, PistolRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/PotionRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/PotionRecipe.cs index 10c05b2fa..1d974a8d4 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/PotionRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/PotionRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a potion. [PublicAPI] +[JsonConverter(typeof(PotionRecipeJsonConverter))] public sealed record PotionRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/PotionRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/PotionRecipeJsonConverter.cs new file mode 100644 index 000000000..ecbc38361 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/PotionRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class PotionRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "potion_recipe"; + + public override PotionRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, PotionRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static PotionRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new PotionRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, PotionRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/Recipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/Recipe.cs index 35db19139..ed2503622 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/Recipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/Recipe.cs @@ -1,4 +1,5 @@ -using GuildWars2.Chat; +using System.Text.Json.Serialization; +using GuildWars2.Chat; using GuildWars2.Hero.Crafting.Disciplines; namespace GuildWars2.Hero.Crafting.Recipes; @@ -8,6 +9,7 @@ namespace GuildWars2.Hero.Crafting.Recipes; [PublicAPI] [Inheritable] [DataTransferObject] +[JsonConverter(typeof(RecipeJsonConverter))] public record Recipe { /// The recipe ID. @@ -48,4 +50,44 @@ public RecipeLink GetChatLink() { return new RecipeLink { RecipeId = Id }; } + + /// + public virtual bool Equals(Recipe? other) + { + if (other is null) return false; + if (ReferenceEquals(this, other)) return true; + return Id == other.Id + && OutputItemId == other.OutputItemId + && OutputItemCount == other.OutputItemCount + && MinRating == other.MinRating + && TimeToCraft.Equals(other.TimeToCraft) + && Disciplines.SequenceEqual(other.Disciplines) + && Flags.Equals(other.Flags) + && Ingredients.SequenceEqual(other.Ingredients) + && ChatLink == other.ChatLink; + } + + /// + public override int GetHashCode() + { + var hashCode = new HashCode(); + hashCode.Add(Id); + hashCode.Add(OutputItemId); + hashCode.Add(OutputItemCount); + hashCode.Add(MinRating); + hashCode.Add(TimeToCraft); + foreach (var discipline in Disciplines) + { + hashCode.Add(discipline); + } + + hashCode.Add(Flags); + foreach (var ingredient in Ingredients) + { + hashCode.Add(ingredient); + } + + hashCode.Add(ChatLink); + return hashCode.ToHashCode(); + } } diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/RecipeFlags.cs b/GW2SDK/Features/Hero/Crafting/Recipes/RecipeFlags.cs index ab56f764f..311382480 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/RecipeFlags.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/RecipeFlags.cs @@ -1,7 +1,12 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Modifiers for crafting recipes. [PublicAPI] +[JsonConverter(typeof(RecipeFlagsJsonConverter))] public sealed record RecipeFlags : Flags { /// Whether the recipe is automatically learned when the player reaches the required discipline level. diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/RecipeFlagsJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/RecipeFlagsJsonConverter.cs new file mode 100644 index 000000000..f79eb8e57 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/RecipeFlagsJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class RecipeFlagsJsonConverter : JsonConverter +{ + public override RecipeFlags? Read( + ref Utf8JsonReader reader, + Type typeToConvert, + JsonSerializerOptions options + ) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write( + Utf8JsonWriter writer, + RecipeFlags value, + JsonSerializerOptions options + ) + { + Write(writer, value); + } + + public static RecipeFlags Read(JsonElement json) + { + return new RecipeFlags + { + AutoLearned = json.GetProperty("auto_learned").GetBoolean(), + LearnedFromItem = json.GetProperty("learned_from_item").GetBoolean(), + Other = json.GetProperty("other").GetList(static value => value.GetStringRequired()) + }; + } + public static void Write(Utf8JsonWriter writer, RecipeFlags value) + { + writer.WriteStartObject(); + writer.WriteBoolean("auto_learned", value.AutoLearned); + writer.WriteBoolean("learned_from_item", value.LearnedFromItem); + writer.WritePropertyName("other"); + writer.WriteStartArray(); + foreach (var other in value.Other) + { + writer.WriteStringValue(other); + } + writer.WriteEndArray(); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/RecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/RecipeJsonConverter.cs new file mode 100644 index 000000000..cd8393df4 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/RecipeJsonConverter.cs @@ -0,0 +1,359 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class RecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorName = "$type"; + + public const string DiscriminatorValue = "recipe"; + + public override bool CanConvert(Type typeToConvert) + { + return typeof(Recipe).IsAssignableFrom(typeToConvert); + } + + public override Recipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write( + Utf8JsonWriter writer, + Recipe value, + JsonSerializerOptions options + ) + { + Write(writer, value); + } + + public static Recipe Read(JsonElement json) + { + if (json.TryGetProperty(DiscriminatorName, out var discriminator)) + { + switch (discriminator.ToString()) + { + case AmuletRecipeJsonConverter.DiscriminatorValue: + return AmuletRecipeJsonConverter.Read(json); + case AxeRecipeJsonConverter.DiscriminatorValue: + return AxeRecipeJsonConverter.Read(json); + case BackpackRecipeJsonConverter.DiscriminatorValue: + return BackpackRecipeJsonConverter.Read(json); + case BagRecipeJsonConverter.DiscriminatorValue: + return BagRecipeJsonConverter.Read(json); + case BootsRecipeJsonConverter.DiscriminatorValue: + return BootsRecipeJsonConverter.Read(json); + case BulkRecipeJsonConverter.DiscriminatorValue: + return BulkRecipeJsonConverter.Read(json); + case CoatRecipeJsonConverter.DiscriminatorValue: + return CoatRecipeJsonConverter.Read(json); + case ComponentRecipeJsonConverter.DiscriminatorValue: + return ComponentRecipeJsonConverter.Read(json); + case ConsumableRecipeJsonConverter.DiscriminatorValue: + return ConsumableRecipeJsonConverter.Read(json); + case DaggerRecipeJsonConverter.DiscriminatorValue: + return DaggerRecipeJsonConverter.Read(json); + case DessertRecipeJsonConverter.DiscriminatorValue: + return DessertRecipeJsonConverter.Read(json); + case DyeRecipeJsonConverter.DiscriminatorValue: + return DyeRecipeJsonConverter.Read(json); + case EarringRecipeJsonConverter.DiscriminatorValue: + return EarringRecipeJsonConverter.Read(json); + case FeastRecipeJsonConverter.DiscriminatorValue: + return FeastRecipeJsonConverter.Read(json); + case FocusRecipeJsonConverter.DiscriminatorValue: + return FocusRecipeJsonConverter.Read(json); + case GuildConsumableRecipeJsonConverter.DiscriminatorValue: + return GuildConsumableRecipeJsonConverter.Read(json); + case GuildDecorationRecipeJsonConverter.DiscriminatorValue: + return GuildDecorationRecipeJsonConverter.Read(json); + case GuildWvwUpgradeRecipeJsonConverter.DiscriminatorValue: + return GuildWvwUpgradeRecipeJsonConverter.Read(json); + case GlovesRecipeJsonConverter.DiscriminatorValue: + return GlovesRecipeJsonConverter.Read(json); + case GreatswordRecipeJsonConverter.DiscriminatorValue: + return GreatswordRecipeJsonConverter.Read(json); + case HeadgearRecipeJsonConverter.DiscriminatorValue: + return HeadgearRecipeJsonConverter.Read(json); + case HarpoonGunRecipeJsonConverter.DiscriminatorValue: + return HarpoonGunRecipeJsonConverter.Read(json); + case IngredientCookingRecipeJsonConverter.DiscriminatorValue: + return IngredientCookingRecipeJsonConverter.Read(json); + case InscriptionRecipeJsonConverter.DiscriminatorValue: + return InscriptionRecipeJsonConverter.Read(json); + case InsigniaRecipeJsonConverter.DiscriminatorValue: + return InsigniaRecipeJsonConverter.Read(json); + case LegendaryComponentRecipeJsonConverter.DiscriminatorValue: + return LegendaryComponentRecipeJsonConverter.Read(json); + case LeggingsRecipeJsonConverter.DiscriminatorValue: + return LeggingsRecipeJsonConverter.Read(json); + case HammerRecipeJsonConverter.DiscriminatorValue: + return HammerRecipeJsonConverter.Read(json); + case LongbowRecipeJsonConverter.DiscriminatorValue: + return LongbowRecipeJsonConverter.Read(json); + case MaceRecipeJsonConverter.DiscriminatorValue: + return MaceRecipeJsonConverter.Read(json); + case MealRecipeJsonConverter.DiscriminatorValue: + return MealRecipeJsonConverter.Read(json); + case PistolRecipeJsonConverter.DiscriminatorValue: + return PistolRecipeJsonConverter.Read(json); + case PotionRecipeJsonConverter.DiscriminatorValue: + return PotionRecipeJsonConverter.Read(json); + case RefinementRecipeJsonConverter.DiscriminatorValue: + return RefinementRecipeJsonConverter.Read(json); + case RefinementEctoplasmRecipeJsonConverter.DiscriminatorValue: + return RefinementEctoplasmRecipeJsonConverter.Read(json); + case RefinementObsidianRecipeJsonConverter.DiscriminatorValue: + return RefinementObsidianRecipeJsonConverter.Read(json); + case RifleRecipeJsonConverter.DiscriminatorValue: + return RifleRecipeJsonConverter.Read(json); + case RingRecipeJsonConverter.DiscriminatorValue: + return RingRecipeJsonConverter.Read(json); + case ScepterRecipeJsonConverter.DiscriminatorValue: + return ScepterRecipeJsonConverter.Read(json); + case SeasoningRecipeJsonConverter.DiscriminatorValue: + return SeasoningRecipeJsonConverter.Read(json); + case ShieldRecipeJsonConverter.DiscriminatorValue: + return ShieldRecipeJsonConverter.Read(json); + case ShortbowRecipeJsonConverter.DiscriminatorValue: + return ShortbowRecipeJsonConverter.Read(json); + case ShouldersRecipeJsonConverter.DiscriminatorValue: + return ShouldersRecipeJsonConverter.Read(json); + case SnackRecipeJsonConverter.DiscriminatorValue: + return SnackRecipeJsonConverter.Read(json); + case SoupRecipeJsonConverter.DiscriminatorValue: + return SoupRecipeJsonConverter.Read(json); + case SpearRecipeJsonConverter.DiscriminatorValue: + return SpearRecipeJsonConverter.Read(json); + case StaffRecipeJsonConverter.DiscriminatorValue: + return StaffRecipeJsonConverter.Read(json); + case SwordRecipeJsonConverter.DiscriminatorValue: + return SwordRecipeJsonConverter.Read(json); + case TorchRecipeJsonConverter.DiscriminatorValue: + return TorchRecipeJsonConverter.Read(json); + case TridentRecipeJsonConverter.DiscriminatorValue: + return TridentRecipeJsonConverter.Read(json); + case UpgradeComponentRecipeJsonConverter.DiscriminatorValue: + return UpgradeComponentRecipeJsonConverter.Read(json); + case WarhornRecipeJsonConverter.DiscriminatorValue: + return WarhornRecipeJsonConverter.Read(json); + } + } + + return new Recipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = + TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = + json.GetProperty("disciplines") + .GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + + public static void Write(Utf8JsonWriter writer, Recipe value) + { + switch (value) + { + case AmuletRecipe amuletRecipe: + AmuletRecipeJsonConverter.Write(writer, amuletRecipe); + break; + case AxeRecipe axeRecipe: + AxeRecipeJsonConverter.Write(writer, axeRecipe); + break; + case BackpackRecipe backpackRecipe: + BackpackRecipeJsonConverter.Write(writer, backpackRecipe); + break; + case BagRecipe bagRecipe: + BagRecipeJsonConverter.Write(writer, bagRecipe); + break; + case BootsRecipe bootsRecipe: + BootsRecipeJsonConverter.Write(writer, bootsRecipe); + break; + case BulkRecipe bulkRecipe: + BulkRecipeJsonConverter.Write(writer, bulkRecipe); + break; + case CoatRecipe coatRecipe: + CoatRecipeJsonConverter.Write(writer, coatRecipe); + break; + case ComponentRecipe componentRecipe: + ComponentRecipeJsonConverter.Write(writer, componentRecipe); + break; + case ConsumableRecipe consumableRecipe: + ConsumableRecipeJsonConverter.Write(writer, consumableRecipe); + break; + case DaggerRecipe daggerRecipe: + DaggerRecipeJsonConverter.Write(writer, daggerRecipe); + break; + case DessertRecipe dessertRecipe: + DessertRecipeJsonConverter.Write(writer, dessertRecipe); + break; + case DyeRecipe dyeRecipe: + DyeRecipeJsonConverter.Write(writer, dyeRecipe); + break; + case EarringRecipe earringRecipe: + EarringRecipeJsonConverter.Write(writer, earringRecipe); + break; + case FeastRecipe feastRecipe: + FeastRecipeJsonConverter.Write(writer, feastRecipe); + break; + case FocusRecipe focusRecipe: + FocusRecipeJsonConverter.Write(writer, focusRecipe); + break; + case GuildConsumableRecipe guildConsumableRecipe: + GuildConsumableRecipeJsonConverter.Write(writer, guildConsumableRecipe); + break; + case GuildDecorationRecipe guildDecorationRecipe: + GuildDecorationRecipeJsonConverter.Write(writer, guildDecorationRecipe); + break; + case GuildWvwUpgradeRecipe guildWvwUpgradeRecipe: + GuildWvwUpgradeRecipeJsonConverter.Write(writer, guildWvwUpgradeRecipe); + break; + case GlovesRecipe glovesRecipe: + GlovesRecipeJsonConverter.Write(writer, glovesRecipe); + break; + case GreatswordRecipe greatswordRecipe: + GreatswordRecipeJsonConverter.Write(writer, greatswordRecipe); + break; + case HeadgearRecipe headgearRecipe: + HeadgearRecipeJsonConverter.Write(writer, headgearRecipe); + break; + case HarpoonGunRecipe harpoonGunRecipe: + HarpoonGunRecipeJsonConverter.Write(writer, harpoonGunRecipe); + break; + case IngredientCookingRecipe ingredientCookingRecipe: + IngredientCookingRecipeJsonConverter.Write(writer, ingredientCookingRecipe); + break; + case InscriptionRecipe inscriptionRecipe: + InscriptionRecipeJsonConverter.Write(writer, inscriptionRecipe); + break; + case InsigniaRecipe insigniaRecipe: + InsigniaRecipeJsonConverter.Write(writer, insigniaRecipe); + break; + case LegendaryComponentRecipe legendaryComponentRecipe: + LegendaryComponentRecipeJsonConverter.Write(writer, legendaryComponentRecipe); + break; + case LeggingsRecipe leggingsRecipe: + LeggingsRecipeJsonConverter.Write(writer, leggingsRecipe); + break; + case HammerRecipe hammerRecipe: + HammerRecipeJsonConverter.Write(writer, hammerRecipe); + break; + case LongbowRecipe longbowRecipe: + LongbowRecipeJsonConverter.Write(writer, longbowRecipe); + break; + case MaceRecipe maceRecipe: + MaceRecipeJsonConverter.Write(writer, maceRecipe); + break; + case MealRecipe mealRecipe: + MealRecipeJsonConverter.Write(writer, mealRecipe); + break; + case PistolRecipe pistolRecipe: + PistolRecipeJsonConverter.Write(writer, pistolRecipe); + break; + case PotionRecipe potionRecipe: + PotionRecipeJsonConverter.Write(writer, potionRecipe); + break; + case RefinementRecipe refinementRecipe: + RefinementRecipeJsonConverter.Write(writer, refinementRecipe); + break; + case RefinementEctoplasmRecipe refinementEctoplasmRecipe: + RefinementEctoplasmRecipeJsonConverter.Write(writer, refinementEctoplasmRecipe); + break; + case RefinementObsidianRecipe refinementObsidianRecipe: + RefinementObsidianRecipeJsonConverter.Write(writer, refinementObsidianRecipe); + break; + case RifleRecipe rifleRecipe: + RifleRecipeJsonConverter.Write(writer, rifleRecipe); + break; + case RingRecipe ringRecipe: + RingRecipeJsonConverter.Write(writer, ringRecipe); + break; + case ScepterRecipe scepterRecipe: + ScepterRecipeJsonConverter.Write(writer, scepterRecipe); + break; + case SeasoningRecipe seasoningRecipe: + SeasoningRecipeJsonConverter.Write(writer, seasoningRecipe); + break; + case ShieldRecipe shieldRecipe: + ShieldRecipeJsonConverter.Write(writer, shieldRecipe); + break; + case ShortbowRecipe shortbowRecipe: + ShortbowRecipeJsonConverter.Write(writer, shortbowRecipe); + break; + case ShouldersRecipe shouldersRecipe: + ShouldersRecipeJsonConverter.Write(writer, shouldersRecipe); + break; + case SnackRecipe snackRecipe: + SnackRecipeJsonConverter.Write(writer, snackRecipe); + break; + case SoupRecipe soupRecipe: + SoupRecipeJsonConverter.Write(writer, soupRecipe); + break; + case SpearRecipe spearRecipe: + SpearRecipeJsonConverter.Write(writer, spearRecipe); + break; + case StaffRecipe staffRecipe: + StaffRecipeJsonConverter.Write(writer, staffRecipe); + break; + case SwordRecipe swordRecipe: + SwordRecipeJsonConverter.Write(writer, swordRecipe); + break; + case TorchRecipe torchRecipe: + TorchRecipeJsonConverter.Write(writer, torchRecipe); + break; + case TridentRecipe tridentRecipe: + TridentRecipeJsonConverter.Write(writer, tridentRecipe); + break; + case UpgradeComponentRecipe upgradeComponentRecipe: + UpgradeComponentRecipeJsonConverter.Write(writer, upgradeComponentRecipe); + break; + case WarhornRecipe warhornRecipe: + WarhornRecipeJsonConverter.Write(writer, warhornRecipe); + break; + default: + writer.WriteStartObject(); + writer.WriteString(DiscriminatorName, DiscriminatorValue); + WriteCommonProperties(writer, value); + writer.WriteEndObject(); + break; + } + } + + public static void WriteCommonProperties(Utf8JsonWriter writer, Recipe value) + { + writer.WriteNumber("id", value.Id); + writer.WriteNumber("output_item_id", value.OutputItemId); + writer.WriteNumber("output_item_count", value.OutputItemCount); + writer.WriteNumber("min_rating", value.MinRating); + writer.WriteNumber("time_to_craft_ms", value.TimeToCraft.TotalMilliseconds); + writer.WritePropertyName("disciplines"); + writer.WriteStartArray(); + foreach (var discipline in value.Disciplines) + { + writer.WriteStringValue(discipline.ToString()); + } + writer.WriteEndArray(); + writer.WritePropertyName("flags"); + RecipeFlagsJsonConverter.Write(writer, value.Flags); + writer.WritePropertyName("ingredients"); + writer.WriteStartArray(); + foreach (var ingredient in value.Ingredients) + { + IngredientJsonConverter.Write(writer, ingredient); + } + writer.WriteEndArray(); + writer.WriteString("chat_link", value.ChatLink); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/RefinementEctoplasmRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/RefinementEctoplasmRecipe.cs index 79972ffe4..9ad5c6396 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/RefinementEctoplasmRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/RefinementEctoplasmRecipe.cs @@ -1,6 +1,9 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting an ascended material that requires Globs of Ectoplasm like a Lump of /// Mithrillium. [PublicAPI] +[JsonConverter(typeof(RefinementEctoplasmRecipeJsonConverter))] public sealed record RefinementEctoplasmRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/RefinementEctoplasmRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/RefinementEctoplasmRecipeJsonConverter.cs new file mode 100644 index 000000000..711a8b0bb --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/RefinementEctoplasmRecipeJsonConverter.cs @@ -0,0 +1,53 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class RefinementEctoplasmRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "refinement_ectoplasm_recipe"; + + public override RefinementEctoplasmRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, RefinementEctoplasmRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static RefinementEctoplasmRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new RefinementEctoplasmRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, RefinementEctoplasmRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/RefinementObsidianRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/RefinementObsidianRecipe.cs index 18b00afbb..1f3b323e3 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/RefinementObsidianRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/RefinementObsidianRecipe.cs @@ -1,6 +1,9 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting an ascended material that requires Obsidian Shards like a Bloodstone /// Brick. [PublicAPI] +[JsonConverter(typeof(RefinementObsidianRecipeJsonConverter))] public sealed record RefinementObsidianRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/RefinementObsidianRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/RefinementObsidianRecipeJsonConverter.cs new file mode 100644 index 000000000..69f03f812 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/RefinementObsidianRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class RefinementObsidianRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "refinement_obsidian_recipe"; + + public override RefinementObsidianRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, RefinementObsidianRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static RefinementObsidianRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new RefinementObsidianRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, RefinementObsidianRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/RefinementRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/RefinementRecipe.cs index d2c64975c..e04403f18 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/RefinementRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/RefinementRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for refining materials. [PublicAPI] +[JsonConverter(typeof(RefinementRecipeJsonConverter))] public sealed record RefinementRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/RefinementRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/RefinementRecipeJsonConverter.cs new file mode 100644 index 000000000..b58b01916 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/RefinementRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class RefinementRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "refinement_recipe"; + + public override RefinementRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, RefinementRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static RefinementRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new RefinementRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, RefinementRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/RifleRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/RifleRecipe.cs index 98f6f9814..61bb04018 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/RifleRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/RifleRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a rifle. [PublicAPI] +[JsonConverter(typeof(RifleRecipeJsonConverter))] public sealed record RifleRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/RifleRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/RifleRecipeJsonConverter.cs new file mode 100644 index 000000000..f69b1cc06 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/RifleRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class RifleRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "rifle_recipe"; + + public override RifleRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, RifleRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static RifleRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new RifleRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, RifleRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/RingRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/RingRecipe.cs index 78e0bcb26..3859ce047 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/RingRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/RingRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a ring. [PublicAPI] +[JsonConverter(typeof(RingRecipeJsonConverter))] public sealed record RingRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/RingRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/RingRecipeJsonConverter.cs new file mode 100644 index 000000000..ccfce7717 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/RingRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class RingRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "ring_recipe"; + + public override RingRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, RingRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static RingRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new RingRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, RingRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/ScepterRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/ScepterRecipe.cs index 5ff8ef4b9..d0ab5c2d2 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/ScepterRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/ScepterRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a scepter. [PublicAPI] +[JsonConverter(typeof(ScepterRecipeJsonConverter))] public sealed record ScepterRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/ScepterRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/ScepterRecipeJsonConverter.cs new file mode 100644 index 000000000..2d7b9574c --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/ScepterRecipeJsonConverter.cs @@ -0,0 +1,52 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class ScepterRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "scepter_recipe"; + + public override ScepterRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, ScepterRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static ScepterRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new ScepterRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, ScepterRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/SeasoningRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/SeasoningRecipe.cs index c36557474..3be79316d 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/SeasoningRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/SeasoningRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting seasoning. [PublicAPI] +[JsonConverter(typeof(SeasoningRecipeJsonConverter))] public sealed record SeasoningRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/SeasoningRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/SeasoningRecipeJsonConverter.cs new file mode 100644 index 000000000..f460200d4 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/SeasoningRecipeJsonConverter.cs @@ -0,0 +1,53 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class SeasoningRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "seasoning_recipe"; + + public override SeasoningRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, SeasoningRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static SeasoningRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new SeasoningRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, SeasoningRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/ShieldRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/ShieldRecipe.cs index 89e8ae5c8..ab9cbb944 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/ShieldRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/ShieldRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a shield. [PublicAPI] +[JsonConverter(typeof(ShieldRecipeJsonConverter))] public sealed record ShieldRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/ShieldRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/ShieldRecipeJsonConverter.cs new file mode 100644 index 000000000..e5a8258ef --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/ShieldRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class ShieldRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "shield_recipe"; + + public override ShieldRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, ShieldRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static ShieldRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new ShieldRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, ShieldRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/ShortBowRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/ShortBowRecipe.cs index e43fae81c..6bc4fbfb5 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/ShortBowRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/ShortBowRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a shortbow. [PublicAPI] +[JsonConverter(typeof(ShortbowRecipeJsonConverter))] public sealed record ShortbowRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/ShortbowRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/ShortbowRecipeJsonConverter.cs new file mode 100644 index 000000000..94828c4c7 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/ShortbowRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class ShortbowRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "shortbow_recipe"; + + public override ShortbowRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, ShortbowRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static ShortbowRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new ShortbowRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, ShortbowRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/ShouldersRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/ShouldersRecipe.cs index 9993205f0..3bb0932e8 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/ShouldersRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/ShouldersRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting shoulders. [PublicAPI] +[JsonConverter(typeof(ShouldersRecipeJsonConverter))] public sealed record ShouldersRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/ShouldersRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/ShouldersRecipeJsonConverter.cs new file mode 100644 index 000000000..4688b331e --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/ShouldersRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class ShouldersRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "shoulders_recipe"; + + public override ShouldersRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, ShouldersRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static ShouldersRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new ShouldersRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, ShouldersRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/SnackRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/SnackRecipe.cs index a7538b939..ec9c9a48f 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/SnackRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/SnackRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; -/// Information about a recipe for crafting a snack. +namespace GuildWars2.Hero.Crafting.Recipes; + +/// Information about a recipe for cooking snacks. [PublicAPI] +[JsonConverter(typeof(SnackRecipeJsonConverter))] public sealed record SnackRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/SnackRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/SnackRecipeJsonConverter.cs new file mode 100644 index 000000000..e4de29c4a --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/SnackRecipeJsonConverter.cs @@ -0,0 +1,53 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class SnackRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "snack_recipe"; + + public override SnackRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, SnackRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static SnackRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new SnackRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, SnackRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/SoupRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/SoupRecipe.cs index 41a9622e5..231dfec55 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/SoupRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/SoupRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; -/// Information about a recipe for crafting soup. +namespace GuildWars2.Hero.Crafting.Recipes; + +/// Information about a recipe for cooking soups. [PublicAPI] +[JsonConverter(typeof(SoupRecipeJsonConverter))] public sealed record SoupRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/SoupRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/SoupRecipeJsonConverter.cs new file mode 100644 index 000000000..257231cf0 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/SoupRecipeJsonConverter.cs @@ -0,0 +1,53 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class SoupRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "soup_recipe"; + + public override SoupRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, SoupRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static SoupRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new SoupRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, SoupRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/SpearRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/SpearRecipe.cs index 76cc9593a..accf6af88 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/SpearRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/SpearRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a spear. [PublicAPI] +[JsonConverter(typeof(SpearRecipeJsonConverter))] public sealed record SpearRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/SpearRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/SpearRecipeJsonConverter.cs new file mode 100644 index 000000000..cbf661bd5 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/SpearRecipeJsonConverter.cs @@ -0,0 +1,52 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class SpearRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "spear_recipe"; + + public override SpearRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, SpearRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static SpearRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new SpearRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, SpearRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/StaffRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/StaffRecipe.cs index 214545df7..cd1e595f6 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/StaffRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/StaffRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a staff. [PublicAPI] +[JsonConverter(typeof(StaffRecipeJsonConverter))] public sealed record StaffRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/StaffRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/StaffRecipeJsonConverter.cs new file mode 100644 index 000000000..7f5fe7bba --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/StaffRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class StaffRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "staff_recipe"; + + public override StaffRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, StaffRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static StaffRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new StaffRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, StaffRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/SwordRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/SwordRecipe.cs index 7c3aa4ea7..499e6f5f5 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/SwordRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/SwordRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a sword. [PublicAPI] +[JsonConverter(typeof(SwordRecipeJsonConverter))] public sealed record SwordRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/SwordRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/SwordRecipeJsonConverter.cs new file mode 100644 index 000000000..f5359bc36 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/SwordRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class SwordRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "sword_recipe"; + + public override SwordRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, SwordRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static SwordRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new SwordRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, SwordRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/TorchRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/TorchRecipe.cs index ecd995e25..b4502c391 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/TorchRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/TorchRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a torch. [PublicAPI] +[JsonConverter(typeof(TorchRecipeJsonConverter))] public sealed record TorchRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/TorchRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/TorchRecipeJsonConverter.cs new file mode 100644 index 000000000..109433123 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/TorchRecipeJsonConverter.cs @@ -0,0 +1,52 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class TorchRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "torch_recipe"; + + public override TorchRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, TorchRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static TorchRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new TorchRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, TorchRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/TridentRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/TridentRecipe.cs index 8cd07a60c..85b6939de 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/TridentRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/TridentRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a trident. [PublicAPI] +[JsonConverter(typeof(TridentRecipeJsonConverter))] public sealed record TridentRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/TridentRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/TridentRecipeJsonConverter.cs new file mode 100644 index 000000000..dc684eb9f --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/TridentRecipeJsonConverter.cs @@ -0,0 +1,52 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class TridentRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "trident_recipe"; + + public override TridentRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, TridentRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static TridentRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new TridentRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, TridentRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} + diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/UpgradeComponentRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/UpgradeComponentRecipe.cs index 2230ee7e2..a48766566 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/UpgradeComponentRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/UpgradeComponentRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting an upgrade component. [PublicAPI] +[JsonConverter(typeof(UpgradeComponentRecipeJsonConverter))] public sealed record UpgradeComponentRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/UpgradeComponentRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/UpgradeComponentRecipeJsonConverter.cs new file mode 100644 index 000000000..b60ef1947 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/UpgradeComponentRecipeJsonConverter.cs @@ -0,0 +1,51 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class UpgradeComponentRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "upgrade_component_recipe"; + + public override UpgradeComponentRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, UpgradeComponentRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static UpgradeComponentRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new UpgradeComponentRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, UpgradeComponentRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/WarhornRecipe.cs b/GW2SDK/Features/Hero/Crafting/Recipes/WarhornRecipe.cs index 81b074b49..87f751016 100644 --- a/GW2SDK/Features/Hero/Crafting/Recipes/WarhornRecipe.cs +++ b/GW2SDK/Features/Hero/Crafting/Recipes/WarhornRecipe.cs @@ -1,5 +1,8 @@ -namespace GuildWars2.Hero.Crafting.Recipes; +using System.Text.Json.Serialization; + +namespace GuildWars2.Hero.Crafting.Recipes; /// Information about a recipe for crafting a warhorn. [PublicAPI] +[JsonConverter(typeof(WarhornRecipeJsonConverter))] public sealed record WarhornRecipe : Recipe; diff --git a/GW2SDK/Features/Hero/Crafting/Recipes/WarhornRecipeJsonConverter.cs b/GW2SDK/Features/Hero/Crafting/Recipes/WarhornRecipeJsonConverter.cs new file mode 100644 index 000000000..b3fef3f19 --- /dev/null +++ b/GW2SDK/Features/Hero/Crafting/Recipes/WarhornRecipeJsonConverter.cs @@ -0,0 +1,52 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using GuildWars2.Hero.Crafting.Disciplines; +using GuildWars2.Json; + +namespace GuildWars2.Hero.Crafting.Recipes; + +internal sealed class WarhornRecipeJsonConverter : JsonConverter +{ + public const string DiscriminatorValue = "warhorn_recipe"; + + public override WarhornRecipe Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + using var json = JsonDocument.ParseValue(ref reader); + return Read(json.RootElement); + } + + public override void Write(Utf8JsonWriter writer, WarhornRecipe value, JsonSerializerOptions options) + { + Write(writer, value); + } + + public static WarhornRecipe Read(JsonElement json) + { + if (!json.GetProperty(RecipeJsonConverter.DiscriminatorName).ValueEquals(DiscriminatorValue)) + { + ThrowHelper.ThrowInvalidDiscriminator(json.GetProperty(RecipeJsonConverter.DiscriminatorName).GetString()); + } + + return new WarhornRecipe + { + Id = json.GetProperty("id").GetInt32(), + OutputItemId = json.GetProperty("output_item_id").GetInt32(), + OutputItemCount = json.GetProperty("output_item_count").GetInt32(), + MinRating = json.GetProperty("min_rating").GetInt32(), + TimeToCraft = TimeSpan.FromMilliseconds(json.GetProperty("time_to_craft_ms").GetDouble()), + Disciplines = json.GetProperty("disciplines").GetList(static value => value.GetEnum()), + Flags = RecipeFlagsJsonConverter.Read(json.GetProperty("flags")), + Ingredients = json.GetProperty("ingredients").GetList(IngredientJsonConverter.Read), + ChatLink = json.GetProperty("chat_link").GetStringRequired() + }; + } + + public static void Write(Utf8JsonWriter writer, WarhornRecipe value) + { + writer.WriteStartObject(); + writer.WriteString(RecipeJsonConverter.DiscriminatorName, DiscriminatorValue); + RecipeJsonConverter.WriteCommonProperties(writer, value); + writer.WriteEndObject(); + } +} +