diff --git a/Pf2eDataScrapper/Pf2eDataScrapper.csproj b/Pf2eDataScrapper/Pf2eDataScrapper.csproj index af748c0..0f35fa8 100644 --- a/Pf2eDataScrapper/Pf2eDataScrapper.csproj +++ b/Pf2eDataScrapper/Pf2eDataScrapper.csproj @@ -11,4 +11,10 @@ + + + PreserveNewest + + + diff --git a/Pf2eDataScrapper/Program.cs b/Pf2eDataScrapper/Program.cs index 3ed0e5d..b8f448e 100644 --- a/Pf2eDataScrapper/Program.cs +++ b/Pf2eDataScrapper/Program.cs @@ -1,3 +1,17 @@ -// See https://aka.ms/new-console-template for more information +using Newtonsoft.Json.Linq; +using Pf2eDataScrapper.SchemaBuilder; + Console.WriteLine("Hello, World!"); +JObject data; + +using (var reader = new StreamReader("test.json")) +{ + data = JObject.Parse(reader.ReadToEnd()); +} + +AbstractSchemaType schema = new ObjectSchemaType(); +schema = schema.Adapt(data); +schema.AddValue(data); + +Console.WriteLine(schema); diff --git a/Pf2eDataScrapper/SchemaBuilder/AbstractSchemaType.cs b/Pf2eDataScrapper/SchemaBuilder/AbstractSchemaType.cs index 5b92be2..f2ecf3f 100644 --- a/Pf2eDataScrapper/SchemaBuilder/AbstractSchemaType.cs +++ b/Pf2eDataScrapper/SchemaBuilder/AbstractSchemaType.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Text; -using System.Threading.Tasks; +using Newtonsoft.Json.Linq; namespace Pf2eDataScrapper.SchemaBuilder { @@ -30,7 +24,7 @@ namespace Pf2eDataScrapper.SchemaBuilder /// /// Value to be checked /// True if value is compatible with current type - public abstract bool IsCompatibleValue(object value); + public abstract bool IsCompatibleValue(JToken? value); /// /// Adapt to new value. @@ -39,21 +33,16 @@ namespace Pf2eDataScrapper.SchemaBuilder /// /// Value to be checked /// Compatible type - public virtual AbstractSchemaType Adapt(object value) + public virtual AbstractSchemaType Adapt(JToken? value) { - if (IsCompatibleValue(value)) - { - return this; - } - - return null; + return IsCompatibleValue(value) ? this : new MixedSchemaType(this); } /// /// Add new possible value. /// /// New value - public abstract void AddValue(object value); + public abstract void AddValue(JToken? value); /// /// String representation of type. @@ -61,14 +50,13 @@ namespace Pf2eDataScrapper.SchemaBuilder /// public abstract string GetTypeName(); - public override abstract string ToString(); - + public abstract override string ToString(); /// /// Tabulation characters. /// - public const string TAB = " "; + public const string Tab = " "; /// /// Limit of elements in list or dictionary to be displayed. @@ -82,7 +70,7 @@ namespace Pf2eDataScrapper.SchemaBuilder /// Indented text public static string Indent(string text) { - return text.Replace("\n", "\n" + TAB); + return text.Replace("\n", "\n" + Tab); } /// @@ -90,37 +78,14 @@ namespace Pf2eDataScrapper.SchemaBuilder /// /// Value to be checked /// String representation of value type - public static string ResolveType(object value) + public static string ResolveType(JToken? value) { - if (value == null) + if (value is null || value.Type is JTokenType.Null or JTokenType.Undefined or JTokenType.None) { return "null"; } - if (value is string) - { - return "string"; - } - if (value is bool) - { - return "bool"; - } - if (value is int) - { - return "int"; - } - if (value is double) - { - return "double"; - } - if (value is List) - { - return "List"; - } - if (value is Dictionary) - { - return "Dictionary"; - } - return "object"; + + return value.Type.ToString().ToLower(); } /// @@ -128,15 +93,17 @@ namespace Pf2eDataScrapper.SchemaBuilder /// /// Value to be checked /// - public static AbstractSchemaType CreateFromValue(object value) + public static AbstractSchemaType CreateFromValue(JToken? value) { + var type = ResolveType(value); - switch (ResolveType(value)) + return type switch { - //case "null": return new NullSchemaType(); - - default: throw new NotImplementedException(); - } + "null" => new EmptySchemaType(), + "array" => new ArraySchemaType(), + "object" => new ObjectSchemaType(), + _ => new StaticSchemaType(type), + }; } } -} +} \ No newline at end of file diff --git a/Pf2eDataScrapper/SchemaBuilder/ArraySchemaType.cs b/Pf2eDataScrapper/SchemaBuilder/ArraySchemaType.cs index af91b17..0b3254c 100644 --- a/Pf2eDataScrapper/SchemaBuilder/ArraySchemaType.cs +++ b/Pf2eDataScrapper/SchemaBuilder/ArraySchemaType.cs @@ -3,6 +3,7 @@ using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; +using Newtonsoft.Json.Linq; namespace Pf2eDataScrapper.SchemaBuilder { @@ -14,10 +15,10 @@ namespace Pf2eDataScrapper.SchemaBuilder protected AbstractSchemaType ElementType = new EmptySchemaType(); - protected List Values = []; + protected List Values = []; - public override void AddValue(object value) + public override void AddValue(JToken? value) { MinCount = Math.Min(MinCount, Values.Count); MaxCount = Math.Max(MaxCount, Values.Count); @@ -30,14 +31,14 @@ namespace Pf2eDataScrapper.SchemaBuilder public override string GetTypeName() { - throw new NotImplementedException(); + return $"array<{ElementType.GetTypeName()}>"; } - public override bool IsCompatibleValue(object value) + public override bool IsCompatibleValue(JToken? value) { var type = ResolveType(value); - if (type == "none") + if (type == "null") { return true; } @@ -58,14 +59,14 @@ namespace Pf2eDataScrapper.SchemaBuilder return true; } - public override AbstractSchemaType Adapt(object value) + public override AbstractSchemaType Adapt(JToken? value) { - if (value is not Array) + if (value is null || value.Type != JTokenType.Array) { throw new Exception("Value is not an array"); } - var array = (object[])value; + var array = (JArray)value; if ((ResolveType(value) == "none") && (ElementType is EmptySchemaType)) { @@ -92,7 +93,7 @@ namespace Pf2eDataScrapper.SchemaBuilder rows.Add($"Length: min={MinCount} max={MaxCount}"); rows.Add("Value type:" + Indent(ElementType.GetTypeName())); - return string.Join("\n" + TAB, rows); + return string.Join("\n" + Tab, rows); } } } diff --git a/Pf2eDataScrapper/SchemaBuilder/EmptySchemaType.cs b/Pf2eDataScrapper/SchemaBuilder/EmptySchemaType.cs index 9e6676d..0de0449 100644 --- a/Pf2eDataScrapper/SchemaBuilder/EmptySchemaType.cs +++ b/Pf2eDataScrapper/SchemaBuilder/EmptySchemaType.cs @@ -1,4 +1,5 @@ -using System; +using Newtonsoft.Json.Linq; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -8,7 +9,7 @@ namespace Pf2eDataScrapper.SchemaBuilder { internal class EmptySchemaType : AbstractSchemaType { - public override AbstractSchemaType Adapt(object value) + public override AbstractSchemaType Adapt(JToken? value) { if (IsCompatibleValue(value)) { @@ -18,7 +19,7 @@ namespace Pf2eDataScrapper.SchemaBuilder return CreateFromValue(value).MarkNullable(); } - public override void AddValue(object value) + public override void AddValue(JToken? value) { } @@ -27,7 +28,7 @@ namespace Pf2eDataScrapper.SchemaBuilder return "empty[] -> null"; } - public override bool IsCompatibleValue(object value) + public override bool IsCompatibleValue(JToken? value) { return ResolveType(value) == "null"; } diff --git a/Pf2eDataScrapper/SchemaBuilder/MixedSchemaType.cs b/Pf2eDataScrapper/SchemaBuilder/MixedSchemaType.cs index 4c75c4d..44c7655 100644 --- a/Pf2eDataScrapper/SchemaBuilder/MixedSchemaType.cs +++ b/Pf2eDataScrapper/SchemaBuilder/MixedSchemaType.cs @@ -1,4 +1,5 @@ -using System; +using Newtonsoft.Json.Linq; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -15,7 +16,7 @@ namespace Pf2eDataScrapper.SchemaBuilder Types.Add(baseType); } - public AbstractSchemaType AddType(object value) + public AbstractSchemaType AddType(JToken value) { var newType = CreateFromValue(value); @@ -24,7 +25,7 @@ namespace Pf2eDataScrapper.SchemaBuilder return newType; } - public AbstractSchemaType? GetCompatibleType(object value) + public AbstractSchemaType? GetCompatibleType(JToken value) { foreach (var type in Types) { @@ -36,7 +37,7 @@ namespace Pf2eDataScrapper.SchemaBuilder return null; } - public override AbstractSchemaType Adapt(object value) + public override AbstractSchemaType Adapt(JToken? value) { var type = GetCompatibleType(value); @@ -48,7 +49,7 @@ namespace Pf2eDataScrapper.SchemaBuilder return this; } - public override void AddValue(object value) + public override void AddValue(JToken? value) { GetCompatibleType(value)?.AddValue(value); } @@ -60,7 +61,7 @@ namespace Pf2eDataScrapper.SchemaBuilder return string.Join("|", typeNames); } - public override bool IsCompatibleValue(object value) => true; + public override bool IsCompatibleValue(JToken? value) => true; /// /// String representation of type. @@ -75,7 +76,7 @@ namespace Pf2eDataScrapper.SchemaBuilder result.Add(Indent(type?.ToString() ?? "")); } - return string.Join("\n" + TAB, result); + return string.Join("\n" + Tab, result); } } } diff --git a/Pf2eDataScrapper/SchemaBuilder/ObjectSchemaType.cs b/Pf2eDataScrapper/SchemaBuilder/ObjectSchemaType.cs index 6ac95b2..6bb281a 100644 --- a/Pf2eDataScrapper/SchemaBuilder/ObjectSchemaType.cs +++ b/Pf2eDataScrapper/SchemaBuilder/ObjectSchemaType.cs @@ -1,4 +1,5 @@ -using System; +using Newtonsoft.Json.Linq; +using System; using System.Collections.Generic; using System.Linq; using System.Text; @@ -12,31 +13,31 @@ namespace Pf2eDataScrapper.SchemaBuilder public Dictionary Fields = []; - public override void AddValue(object value) + public override void AddValue(JToken? value) { - if (value == null) + if (value is null || ResolveType(value) == "null") { MarkNullable(); return; } - var dict = (Dictionary)value; + var obj = (JObject)value; - if (dict.Keys.Count == 0) + if (obj.Count == 0) { MarkNullable(); return; - } - + } + foreach (var field in Fields) { - if (!dict.ContainsKey(field.Key)) + if (!obj.ContainsKey(field.Key)) { field.Value.MarkNullable(); } } - foreach (var field in dict) + foreach (var field in obj) { Fields[field.Key].AddValue(field.Value); } @@ -44,32 +45,38 @@ namespace Pf2eDataScrapper.SchemaBuilder public override string GetTypeName() { - throw new NotImplementedException(); + return "object"; } - public override bool IsCompatibleValue(object value) + public override bool IsCompatibleValue(JToken? value) { return (ResolveType(value) == "object") || (ResolveType(value) == "null"); } - public override AbstractSchemaType Adapt(object value) + public override AbstractSchemaType Adapt(JToken? value) { if (!IsCompatibleValue(value)) { return new MixedSchemaType(this); } - Dictionary dict = (Dictionary)value; + if (value is null) + { + return MarkNullable(); + } + + var obj = (JObject)value; foreach (var field in Fields) { - if (!dict.ContainsKey(field.Key)) + if (!obj.ContainsKey(field.Key)) { field.Value.MarkNullable(); } } - foreach (var field in dict) { + foreach (var field in obj) + { if (!Fields.ContainsKey(field.Key)) { Fields[field.Key] = CreateFromValue(field.Value); @@ -79,6 +86,8 @@ namespace Pf2eDataScrapper.SchemaBuilder Fields[field.Key].MarkNullable(); } } + + Fields[field.Key].Adapt(field.Value); } IsFresh = false; diff --git a/Pf2eDataScrapper/SchemaBuilder/StaticSchemaType.cs b/Pf2eDataScrapper/SchemaBuilder/StaticSchemaType.cs index b8a39c3..25e5fdd 100644 --- a/Pf2eDataScrapper/SchemaBuilder/StaticSchemaType.cs +++ b/Pf2eDataScrapper/SchemaBuilder/StaticSchemaType.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using Newtonsoft.Json.Linq; namespace Pf2eDataScrapper.SchemaBuilder { @@ -10,9 +6,9 @@ namespace Pf2eDataScrapper.SchemaBuilder { protected string Type = type; - protected Dictionary Values = []; + protected Dictionary Values = []; - public override void AddValue(object value) + public override void AddValue(JToken? value) { Values.TryAdd(value, 0); @@ -24,27 +20,27 @@ namespace Pf2eDataScrapper.SchemaBuilder return Type; } - public override bool IsCompatibleValue(object value) + public override bool IsCompatibleValue(JToken? value) { return ResolveType(value) == Type; } public override string ToString() { - string result = Type; - int count = Values.Count; + var result = Type; + var count = Values.Count; result += $" (different values: {count})"; - if ((count <= ElementLimit) && (count > 0)) + if (count is > ElementLimit or <= 0) { - result += "\n"; - foreach (var value in Values) - { - result += $"{TAB}{value.Key} (x{value.Value})\n"; - } + return result.Trim(); } + result += "\n"; + + result = Values.Aggregate(result, (current, value) => current + $"{Tab}{value.Key} (x{value.Value})\n"); + return result.Trim(); } } diff --git a/Pf2eDataScrapper/test.json b/Pf2eDataScrapper/test.json new file mode 100644 index 0000000..e6c936f --- /dev/null +++ b/Pf2eDataScrapper/test.json @@ -0,0 +1,239 @@ +{ + "_id": "XwfcJuskrhI9GIjX", + "img": "systems/pf2e/icons/classes/alchemist.webp", + "name": "Alchemist", + "system": { + "ancestryFeatLevels": { + "value": [ + 1, + 5, + 9, + 13, + 17 + ] + }, + "attacks": { + "advanced": 0, + "martial": 0, + "other": { + "name": "Alchemical Bombs", + "rank": 1 + }, + "simple": 1, + "unarmed": 1 + }, + "classFeatLevels": { + "value": [ + 1, + 2, + 4, + 6, + 8, + 10, + 12, + 14, + 16, + 18, + 20 + ] + }, + "defenses": { + "heavy": 0, + "light": 1, + "medium": 1, + "unarmored": 1 + }, + "description": { + "value": "

There's no sight more beautiful to you than a strange brew bubbling in a beaker, and you consume your ingenious elixirs with abandon. You're fascinated by uncovering the secrets of science and the natural world, and you're constantly experimenting in your lab or on the go with inventive concoctions for every eventuality. You are fearless in the face of risk, hurling explosive or toxic creations at your foes. Your unique path toward greatness is lined with alchemical brews that push your mind and body to their limits.

\n

@UUID[Compendium.pf2e.journals.JournalEntry.kzxu2dI7tFxv6Ix6.JournalEntryPage.Om4e4tsgNWrComie]{Alchemist}

" + }, + "generalFeatLevels": { + "value": [ + 3, + 7, + 11, + 15, + 19 + ] + }, + "hp": 8, + "items": { + "4bdrn": { + "img": "icons/tools/laboratory/mortar-liquid-pink.webp", + "level": 1, + "name": "Alchemy", + "uuid": "Compendium.pf2e.classfeatures.Item.Alchemy" + }, + "CJ528": { + "img": "icons/magic/symbols/question-stone-yellow.webp", + "level": 11, + "name": "Chemical Hardiness", + "uuid": "Compendium.pf2e.classfeatures.Item.Chemical Hardiness" + }, + "JK2gV": { + "img": "icons/consumables/potions/potion-round-stopped-yellow-green.webp", + "level": 15, + "name": "Alchemical Weapon Mastery", + "uuid": "Compendium.pf2e.classfeatures.Item.Alchemical Weapon Mastery" + }, + "NY2Su": { + "img": "icons/creatures/eyes/human-single-blue.webp", + "level": 9, + "name": "Perception Expertise", + "uuid": "Compendium.pf2e.classfeatures.Item.Perception Expertise" + }, + "Q1cDZ": { + "img": "icons/magic/symbols/question-stone-yellow.webp", + "level": 15, + "name": "Explosion Dodger", + "uuid": "Compendium.pf2e.classfeatures.Item.Explosion Dodger" + }, + "SWNkG": { + "img": "icons/equipment/chest/breastplate-collared-steel.webp", + "level": 19, + "name": "Medium Armor Mastery", + "uuid": "Compendium.pf2e.classfeatures.Item.Medium Armor Mastery" + }, + "aay1e": { + "img": "systems/pf2e/icons/features/classes/walls-of-will.webp", + "level": 7, + "name": "Will Expertise", + "uuid": "Compendium.pf2e.classfeatures.Item.Will Expertise" + }, + "b3ohx": { + "img": "icons/magic/symbols/question-stone-yellow.webp", + "level": 1, + "name": "Research Field", + "uuid": "Compendium.pf2e.classfeatures.Item.Research Field" + }, + "cNnDR": { + "img": "icons/equipment/chest/breastplate-quilted-brown.webp", + "level": 13, + "name": "Medium Armor Expertise", + "uuid": "Compendium.pf2e.classfeatures.Item.Medium Armor Expertise" + }, + "enfdl": { + "img": "systems/pf2e/icons/features/classes/weapon-specialization.webp", + "level": 13, + "name": "Weapon Specialization", + "uuid": "Compendium.pf2e.classfeatures.Item.Weapon Specialization" + }, + "f4a0p": { + "img": "icons/magic/light/orbs-hand-gray.webp", + "level": 9, + "name": "Double Brew", + "uuid": "Compendium.pf2e.classfeatures.Item.Double Brew" + }, + "j5xnl": { + "img": "systems/pf2e/icons/features/classes/alchemical-weapon-expertise.webp", + "level": 7, + "name": "Alchemical Weapon Expertise", + "uuid": "Compendium.pf2e.classfeatures.Item.Alchemical Weapon Expertise" + }, + "l170j": { + "img": "icons/magic/symbols/question-stone-yellow.webp", + "level": 13, + "name": "(Choice) Greater Field Discovery", + "uuid": "Compendium.pf2e.classfeatures.Item.Greater Field Discovery" + }, + "plldb": { + "img": "icons/consumables/potions/potion-round-corked-glowing-green.webp", + "level": 9, + "name": "Alchemical Expertise", + "uuid": "Compendium.pf2e.classfeatures.Item.Alchemical Expertise" + }, + "rpCw8": { + "img": "icons/magic/symbols/question-stone-yellow.webp", + "level": 17, + "name": "Abundant Vials", + "uuid": "Compendium.pf2e.classfeatures.Item.Abundant Vials" + }, + "vOE57": { + "img": "icons/magic/symbols/question-stone-yellow.webp", + "level": 11, + "name": "Advanced Vials", + "uuid": "Compendium.pf2e.classfeatures.Item.Advanced Vials" + }, + "xzcdr": { + "img": "icons/magic/symbols/question-stone-yellow.webp", + "level": 5, + "name": "Field Discovery", + "uuid": "Compendium.pf2e.classfeatures.Item.Field Discovery" + }, + "yc9ak": { + "img": "icons/commodities/materials/bowl-powder-teal.webp", + "level": 5, + "name": "Powerful Alchemy", + "uuid": "Compendium.pf2e.classfeatures.Item.Powerful Alchemy" + }, + "yy1uz": { + "img": "icons/tools/cooking/mortar-stone-yellow.webp", + "level": 17, + "name": "Alchemical Mastery", + "uuid": "Compendium.pf2e.classfeatures.Item.Alchemical Mastery" + } + }, + "keyAbility": { + "value": [ + "int" + ] + }, + "perception": 1, + "publication": { + "license": "ORC", + "remaster": true, + "title": "Pathfinder Player Core 2" + }, + "rules": [ + { + "key": "ActiveEffectLike", + "mode": "upgrade", + "path": "system.proficiencies.attacks.weapon-base-alchemical-bomb.rank", + "value": 1 + } + ], + "savingThrows": { + "fortitude": 2, + "reflex": 2, + "will": 1 + }, + "skillFeatLevels": { + "value": [ + 2, + 4, + 6, + 8, + 10, + 12, + 14, + 16, + 18, + 20 + ] + }, + "skillIncreaseLevels": { + "value": [ + 3, + 5, + 7, + 9, + 11, + 13, + 15, + 17, + 19 + ] + }, + "spellcasting": 0, + "trainedSkills": { + "additional": 3, + "value": [ + "crafting" + ] + }, + "traits": { + "rarity": "common", + "value": [] + } + }, + "type": "class" +} \ No newline at end of file