Переход на Newtonsoft.Json

This commit is contained in:
2025-03-27 23:59:24 +04:00
parent e2f740c29d
commit d82df0729b
9 changed files with 340 additions and 106 deletions
+6
View File
@@ -11,4 +11,10 @@
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
</ItemGroup> </ItemGroup>
<ItemGroup>
<None Update="test.json">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
</Project> </Project>
+15 -1
View File
@@ -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!"); 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);
@@ -1,10 +1,4 @@
using System; using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.CompilerServices;
using System.Text;
using System.Threading.Tasks;
namespace Pf2eDataScrapper.SchemaBuilder namespace Pf2eDataScrapper.SchemaBuilder
{ {
@@ -30,7 +24,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
/// </summary> /// </summary>
/// <param name="value">Value to be checked</param> /// <param name="value">Value to be checked</param>
/// <returns>True if value is compatible with current type</returns> /// <returns>True if value is compatible with current type</returns>
public abstract bool IsCompatibleValue(object value); public abstract bool IsCompatibleValue(JToken? value);
/// <summary> /// <summary>
/// Adapt to new value. /// Adapt to new value.
@@ -39,21 +33,16 @@ namespace Pf2eDataScrapper.SchemaBuilder
/// </summary> /// </summary>
/// <param name="value">Value to be checked</param> /// <param name="value">Value to be checked</param>
/// <returns>Compatible type</returns> /// <returns>Compatible type</returns>
public virtual AbstractSchemaType Adapt(object value) public virtual AbstractSchemaType Adapt(JToken? value)
{ {
if (IsCompatibleValue(value)) return IsCompatibleValue(value) ? this : new MixedSchemaType(this);
{
return this;
}
return null;
} }
/// <summary> /// <summary>
/// Add new possible value. /// Add new possible value.
/// </summary> /// </summary>
/// <param name="value">New value</param> /// <param name="value">New value</param>
public abstract void AddValue(object value); public abstract void AddValue(JToken? value);
/// <summary> /// <summary>
/// String representation of type. /// String representation of type.
@@ -61,14 +50,13 @@ namespace Pf2eDataScrapper.SchemaBuilder
/// <returns></returns> /// <returns></returns>
public abstract string GetTypeName(); public abstract string GetTypeName();
public override abstract string ToString(); public abstract override string ToString();
/// <summary> /// <summary>
/// Tabulation characters. /// Tabulation characters.
/// </summary> /// </summary>
public const string TAB = " "; public const string Tab = " ";
/// <summary> /// <summary>
/// Limit of elements in list or dictionary to be displayed. /// Limit of elements in list or dictionary to be displayed.
@@ -82,7 +70,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
/// <returns>Indented text</returns> /// <returns>Indented text</returns>
public static string Indent(string text) public static string Indent(string text)
{ {
return text.Replace("\n", "\n" + TAB); return text.Replace("\n", "\n" + Tab);
} }
/// <summary> /// <summary>
@@ -90,37 +78,14 @@ namespace Pf2eDataScrapper.SchemaBuilder
/// </summary> /// </summary>
/// <param name="value">Value to be checked</param> /// <param name="value">Value to be checked</param>
/// <returns>String representation of value type</returns> /// <returns>String representation of value type</returns>
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"; return "null";
} }
if (value is string)
{ return value.Type.ToString().ToLower();
return "string";
}
if (value is bool)
{
return "bool";
}
if (value is int)
{
return "int";
}
if (value is double)
{
return "double";
}
if (value is List<object>)
{
return "List<object>";
}
if (value is Dictionary<string, object>)
{
return "Dictionary<string, object>";
}
return "object";
} }
/// <summary> /// <summary>
@@ -128,15 +93,17 @@ namespace Pf2eDataScrapper.SchemaBuilder
/// </summary> /// </summary>
/// <param name="value">Value to be checked</param> /// <param name="value">Value to be checked</param>
/// <returns></returns> /// <returns></returns>
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(); "null" => new EmptySchemaType(),
"array" => new ArraySchemaType(),
default: throw new NotImplementedException(); "object" => new ObjectSchemaType(),
} _ => new StaticSchemaType(type),
};
} }
} }
} }
@@ -3,6 +3,7 @@ using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Newtonsoft.Json.Linq;
namespace Pf2eDataScrapper.SchemaBuilder namespace Pf2eDataScrapper.SchemaBuilder
{ {
@@ -14,10 +15,10 @@ namespace Pf2eDataScrapper.SchemaBuilder
protected AbstractSchemaType ElementType = new EmptySchemaType(); protected AbstractSchemaType ElementType = new EmptySchemaType();
protected List<object> Values = []; protected List<JToken?> Values = [];
public override void AddValue(object value) public override void AddValue(JToken? value)
{ {
MinCount = Math.Min(MinCount, Values.Count); MinCount = Math.Min(MinCount, Values.Count);
MaxCount = Math.Max(MaxCount, Values.Count); MaxCount = Math.Max(MaxCount, Values.Count);
@@ -30,14 +31,14 @@ namespace Pf2eDataScrapper.SchemaBuilder
public override string GetTypeName() 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); var type = ResolveType(value);
if (type == "none") if (type == "null")
{ {
return true; return true;
} }
@@ -58,14 +59,14 @@ namespace Pf2eDataScrapper.SchemaBuilder
return true; 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"); throw new Exception("Value is not an array");
} }
var array = (object[])value; var array = (JArray)value;
if ((ResolveType(value) == "none") && (ElementType is EmptySchemaType)) if ((ResolveType(value) == "none") && (ElementType is EmptySchemaType))
{ {
@@ -92,7 +93,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
rows.Add($"Length: min={MinCount} max={MaxCount}"); rows.Add($"Length: min={MinCount} max={MaxCount}");
rows.Add("Value type:" + Indent(ElementType.GetTypeName())); rows.Add("Value type:" + Indent(ElementType.GetTypeName()));
return string.Join("\n" + TAB, rows); return string.Join("\n" + Tab, rows);
} }
} }
} }
@@ -1,4 +1,5 @@
using System; using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -8,7 +9,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
{ {
internal class EmptySchemaType : AbstractSchemaType internal class EmptySchemaType : AbstractSchemaType
{ {
public override AbstractSchemaType Adapt(object value) public override AbstractSchemaType Adapt(JToken? value)
{ {
if (IsCompatibleValue(value)) if (IsCompatibleValue(value))
{ {
@@ -18,7 +19,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
return CreateFromValue(value).MarkNullable(); 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"; return "empty[] -> null";
} }
public override bool IsCompatibleValue(object value) public override bool IsCompatibleValue(JToken? value)
{ {
return ResolveType(value) == "null"; return ResolveType(value) == "null";
} }
@@ -1,4 +1,5 @@
using System; using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -15,7 +16,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
Types.Add(baseType); Types.Add(baseType);
} }
public AbstractSchemaType AddType(object value) public AbstractSchemaType AddType(JToken value)
{ {
var newType = CreateFromValue(value); var newType = CreateFromValue(value);
@@ -24,7 +25,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
return newType; return newType;
} }
public AbstractSchemaType? GetCompatibleType(object value) public AbstractSchemaType? GetCompatibleType(JToken value)
{ {
foreach (var type in Types) foreach (var type in Types)
{ {
@@ -36,7 +37,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
return null; return null;
} }
public override AbstractSchemaType Adapt(object value) public override AbstractSchemaType Adapt(JToken? value)
{ {
var type = GetCompatibleType(value); var type = GetCompatibleType(value);
@@ -48,7 +49,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
return this; return this;
} }
public override void AddValue(object value) public override void AddValue(JToken? value)
{ {
GetCompatibleType(value)?.AddValue(value); GetCompatibleType(value)?.AddValue(value);
} }
@@ -60,7 +61,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
return string.Join("|", typeNames); return string.Join("|", typeNames);
} }
public override bool IsCompatibleValue(object value) => true; public override bool IsCompatibleValue(JToken? value) => true;
/// <summary> /// <summary>
/// String representation of type. /// String representation of type.
@@ -75,7 +76,7 @@ namespace Pf2eDataScrapper.SchemaBuilder
result.Add(Indent(type?.ToString() ?? "")); result.Add(Indent(type?.ToString() ?? ""));
} }
return string.Join("\n" + TAB, result); return string.Join("\n" + Tab, result);
} }
} }
} }
@@ -1,4 +1,5 @@
using System; using Newtonsoft.Json.Linq;
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Text; using System.Text;
@@ -12,17 +13,17 @@ namespace Pf2eDataScrapper.SchemaBuilder
public Dictionary<string, AbstractSchemaType> Fields = []; public Dictionary<string, AbstractSchemaType> Fields = [];
public override void AddValue(object value) public override void AddValue(JToken? value)
{ {
if (value == null) if (value is null || ResolveType(value) == "null")
{ {
MarkNullable(); MarkNullable();
return; return;
} }
var dict = (Dictionary<string, object>)value; var obj = (JObject)value;
if (dict.Keys.Count == 0) if (obj.Count == 0)
{ {
MarkNullable(); MarkNullable();
return; return;
@@ -30,13 +31,13 @@ namespace Pf2eDataScrapper.SchemaBuilder
foreach (var field in Fields) foreach (var field in Fields)
{ {
if (!dict.ContainsKey(field.Key)) if (!obj.ContainsKey(field.Key))
{ {
field.Value.MarkNullable(); field.Value.MarkNullable();
} }
} }
foreach (var field in dict) foreach (var field in obj)
{ {
Fields[field.Key].AddValue(field.Value); Fields[field.Key].AddValue(field.Value);
} }
@@ -44,32 +45,38 @@ namespace Pf2eDataScrapper.SchemaBuilder
public override string GetTypeName() 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"); return (ResolveType(value) == "object") || (ResolveType(value) == "null");
} }
public override AbstractSchemaType Adapt(object value) public override AbstractSchemaType Adapt(JToken? value)
{ {
if (!IsCompatibleValue(value)) if (!IsCompatibleValue(value))
{ {
return new MixedSchemaType(this); return new MixedSchemaType(this);
} }
Dictionary<string, object> dict = (Dictionary<string, object>)value; if (value is null)
{
return MarkNullable();
}
var obj = (JObject)value;
foreach (var field in Fields) foreach (var field in Fields)
{ {
if (!dict.ContainsKey(field.Key)) if (!obj.ContainsKey(field.Key))
{ {
field.Value.MarkNullable(); field.Value.MarkNullable();
} }
} }
foreach (var field in dict) { foreach (var field in obj)
{
if (!Fields.ContainsKey(field.Key)) if (!Fields.ContainsKey(field.Key))
{ {
Fields[field.Key] = CreateFromValue(field.Value); Fields[field.Key] = CreateFromValue(field.Value);
@@ -79,6 +86,8 @@ namespace Pf2eDataScrapper.SchemaBuilder
Fields[field.Key].MarkNullable(); Fields[field.Key].MarkNullable();
} }
} }
Fields[field.Key].Adapt(field.Value);
} }
IsFresh = false; IsFresh = false;
@@ -1,8 +1,4 @@
using System; using Newtonsoft.Json.Linq;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Pf2eDataScrapper.SchemaBuilder namespace Pf2eDataScrapper.SchemaBuilder
{ {
@@ -10,9 +6,9 @@ namespace Pf2eDataScrapper.SchemaBuilder
{ {
protected string Type = type; protected string Type = type;
protected Dictionary<object, int> Values = []; protected Dictionary<JToken?, int> Values = [];
public override void AddValue(object value) public override void AddValue(JToken? value)
{ {
Values.TryAdd(value, 0); Values.TryAdd(value, 0);
@@ -24,27 +20,27 @@ namespace Pf2eDataScrapper.SchemaBuilder
return Type; return Type;
} }
public override bool IsCompatibleValue(object value) public override bool IsCompatibleValue(JToken? value)
{ {
return ResolveType(value) == Type; return ResolveType(value) == Type;
} }
public override string ToString() public override string ToString()
{ {
string result = Type; var result = Type;
int count = Values.Count; var count = Values.Count;
result += $" (different values: {count})"; result += $" (different values: {count})";
if ((count <= ElementLimit) && (count > 0)) if (count is > ElementLimit or <= 0)
{ {
result += "\n"; return result.Trim();
foreach (var value in Values)
{
result += $"{TAB}{value.Key} (x{value.Value})\n";
}
} }
result += "\n";
result = Values.Aggregate(result, (current, value) => current + $"{Tab}{value.Key} (x{value.Value})\n");
return result.Trim(); return result.Trim();
} }
} }
+239
View File
@@ -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": "<p><em>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.</em></p>\n<p>@UUID[Compendium.pf2e.journals.JournalEntry.kzxu2dI7tFxv6Ix6.JournalEntryPage.Om4e4tsgNWrComie]{Alchemist}</p>"
},
"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"
}