C
C#•2mo ago
GDcheerios

How to Deserialize dynamic types from json

I'm making a game where users will store multiple different types of items. My Question is when deserializing an item from json it will just deserialize to the given type attribute in the json. example: I want to turn Superhero character json into superhero, but instead it becomes character.
36 Replies
Pobiega
Pobiega•2mo ago
You're looking for the term "polymorphic", and System.Text.Json has built in support for this via [JsonDerivedType] and [JsonPolymorphic(TypeDiscriminatorPropertyName = "type")] See the following simple example
[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")]
[JsonDerivedType(typeof(BarChart), nameof(ChartType.Bar))]
[JsonDerivedType(typeof(LineChart), nameof(ChartType.Line))]
public abstract record Chart(string? YAxisLabel = null);

public record BarChart(ICollection<string> Columns, string? YAxisLabel = null) : Chart(YAxisLabel);

public record LineChart(ICollection<string> Columns, string? YAxisLabel = null) : Chart(YAxisLabel);
[JsonPolymorphic(TypeDiscriminatorPropertyName = "type")]
[JsonDerivedType(typeof(BarChart), nameof(ChartType.Bar))]
[JsonDerivedType(typeof(LineChart), nameof(ChartType.Line))]
public abstract record Chart(string? YAxisLabel = null);

public record BarChart(ICollection<string> Columns, string? YAxisLabel = null) : Chart(YAxisLabel);

public record LineChart(ICollection<string> Columns, string? YAxisLabel = null) : Chart(YAxisLabel);
so if I have a List<Chart>, I can store LineCharts and BarCharts in it, and they will serialize with a type property
GDcheerios
GDcheeriosOP•2mo ago
that is amazing
Pobiega
Pobiega•2mo ago
and the classes can be as different as they need to be
GDcheerios
GDcheeriosOP•2mo ago
wow
Pobiega
Pobiega•2mo ago
all you need to do is decorate the base class with the correct attributes
GDcheerios
GDcheeriosOP•2mo ago
I gotcha thanks man
Pobiega
Pobiega•2mo ago
np
GDcheerios
GDcheeriosOP•2mo ago
I think you just solved the worlds hardest problem
Pobiega
Pobiega•2mo ago
😄
GDcheerios
GDcheeriosOP•2mo ago
😭
Pobiega
Pobiega•2mo ago
No, I've just done exactly this many times and I remember when STJ first got these attributes and what a relief it was
GDcheerios
GDcheeriosOP•2mo ago
oh wow yeah I can imagine
Pobiega
Pobiega•2mo ago
JsonPolymorphic isnt strictly needed, but if you skip it, or dont assign a TypeDescriminatorPropertyName, it will default to $type so its pretty common to override it
GDcheerios
GDcheeriosOP•2mo ago
so in the class that overwrites you'd just add it? like this:
using System.Text.Json.Serialization;
using GentrysQuest.Game.Content.Skills;
using GentrysQuest.Game.Entity;

namespace GentrysQuest.Game.Content.Characters
{
[JsonDerivedType(typeof(GMoney))]
public class GMoney : Character
{
public GMoney()
{
Name = "GMoney";
StarRating = new StarRating(1);
Description = "Hy-plains Drifter.";

TextureMapping = new();
TextureMapping.Add("Idle", "characters_gmoney_idle.png");
TextureMapping.Add("Icon", "characters_gmoney_idle.png");

Stats.Health.Point = 2;
Stats.Attack.Point = 2;

Secondary = new FrisbeeThrow();
}
}
}
using System.Text.Json.Serialization;
using GentrysQuest.Game.Content.Skills;
using GentrysQuest.Game.Entity;

namespace GentrysQuest.Game.Content.Characters
{
[JsonDerivedType(typeof(GMoney))]
public class GMoney : Character
{
public GMoney()
{
Name = "GMoney";
StarRating = new StarRating(1);
Description = "Hy-plains Drifter.";

TextureMapping = new();
TextureMapping.Add("Idle", "characters_gmoney_idle.png");
TextureMapping.Add("Icon", "characters_gmoney_idle.png");

Stats.Health.Point = 2;
Stats.Attack.Point = 2;

Secondary = new FrisbeeThrow();
}
}
}
Pobiega
Pobiega•2mo ago
No, you set the attributes on the base class so Character in your case and JsonDerivedType takes two parameters, the typeof(YourSubclass) and also a string that is its discriminato value, ie the actual value written to the type field nameof(YourSubClass) will be fine in most cases just make sure its unique
GDcheerios
GDcheeriosOP•2mo ago
is there a trick to if you've got 1000 characters/items? I don't but I know I'll be adding quite a bit. It would be a little strange adding so many above the class
Pobiega
Pobiega•2mo ago
There might be a source generator that can do it, but I'd recommend looking at your design instead in your example above, this doesnt need to be a subclass its just a constructor with values functionally, a GMoney isnt any different from a Character, once the values are set up properly
GDcheerios
GDcheeriosOP•2mo ago
correct, it is just a basic character right now. This character has a little bit more complexity to it.
public class BraydenMesserschmidt : Character
{
public BraydenMesserschmidt()
{
Name = "Brayden Messerschmidt";
StarRating = new StarRating(5);
Description = "An osu player who formed a contract with ppy(Dean Herbert) to not talk to females.";

Stats.Speed.Point = 1;
Stats.AttackSpeed.Point = 1;
Stats.CritRate.Point = 1;
Stats.CritDamage.Point = 1;

Secondary = new CircleThrow();
Utility = new Teleport();

OnUpdateStats += checkWeapon;

TextureMapping = new();
TextureMapping.Add("Icon", "characters_brayden_idle.png");
TextureMapping.Add("Idle", "characters_brayden_idle.png");

AudioMapping.Add("Spawn", "Brayden_Messerschmidt_Spawn.mp3");
AudioMapping.Add("Damage", "Brayden_Messerschmidt_Damage.mp3");
}

private void checkWeapon()
{
Logger.Log("I'm checking the waepon");
RemoveEffect("Chosen One");

if (Weapon != null && Weapon.GetType() == typeof(BraydensOsuPen))
{
AddEffect(new ChosenOne
{
Stack = 1 + Difficulty
});
}
}
}
public class BraydenMesserschmidt : Character
{
public BraydenMesserschmidt()
{
Name = "Brayden Messerschmidt";
StarRating = new StarRating(5);
Description = "An osu player who formed a contract with ppy(Dean Herbert) to not talk to females.";

Stats.Speed.Point = 1;
Stats.AttackSpeed.Point = 1;
Stats.CritRate.Point = 1;
Stats.CritDamage.Point = 1;

Secondary = new CircleThrow();
Utility = new Teleport();

OnUpdateStats += checkWeapon;

TextureMapping = new();
TextureMapping.Add("Icon", "characters_brayden_idle.png");
TextureMapping.Add("Idle", "characters_brayden_idle.png");

AudioMapping.Add("Spawn", "Brayden_Messerschmidt_Spawn.mp3");
AudioMapping.Add("Damage", "Brayden_Messerschmidt_Damage.mp3");
}

private void checkWeapon()
{
Logger.Log("I'm checking the waepon");
RemoveEffect("Chosen One");

if (Weapon != null && Weapon.GetType() == typeof(BraydensOsuPen))
{
AddEffect(new ChosenOne
{
Stack = 1 + Difficulty
});
}
}
}
But I do want to make my code as best as it can be, so if you have any reccomendations I'm all ears.
Pobiega
Pobiega•2mo ago
Yea that might be a little more complicated to turn into pure data, but its doable. The CheckWeapon would need to be turned into a class, and you could serialize that too. Its all serialization 😄
GDcheerios
GDcheeriosOP•2mo ago
😭 I'll just add it to Base class
Pobiega
Pobiega•2mo ago
another thing is that you'll likely want to separate your actual "the game is running" classes from your serialization classes at least if you are to rely on JSON json doesnt handle reference loops very well, and instead you'd use ID lookups but thats a bad model for your actual game code, where you certainly want to use references
GDcheerios
GDcheeriosOP•2mo ago
it is a bit weird, as the game will support online and offline via game saves, and the online database. the game uses nosql
Pobiega
Pobiega•2mo ago
I have not worked a lot with nosql databases, but I'd imagine you cant just stick a complex C# class with several references in there without configuration?
GDcheerios
GDcheeriosOP•2mo ago
oh yeah no, the data is serialized then put into the items table.
Pobiega
Pobiega•2mo ago
So that makes me even further suggest you implement a storage model and a game model, and you write code that can convert between the two hidden bonus, this lets you very easily make the game moddable 🙂 if most of your games data is literally just data files, users can easily make their own characters, levels, items etc
GDcheerios
GDcheeriosOP•2mo ago
thats kinda what I was going for except in coding sense.
Pobiega
Pobiega•2mo ago
I kinda see that, but if its code then at the very least they need to recompile your game to make a mod
GDcheerios
GDcheeriosOP•2mo ago
I don't really want modding, just cause it's supposed to be mostly online, and modded items could cause a problem.
Pobiega
Pobiega•2mo ago
For an online game, you cant trust the client at all so all the client gets to do is tell you stuff like "I want to give item 5123125 in my inventory to player XXXX" it never ever gets to say "oh I just got a new item, here are its stats" thats strictly in the server
GDcheerios
GDcheeriosOP•2mo ago
ah ah ah I see I miss spoke the game client handles all the item obtaining and such, but when an item is obtained/modified/removed it tells the server, and it updates the database accordingly and gives players ratings based on their items. then when the player re logs in another time it will have all the item data
Pobiega
Pobiega•2mo ago
Okay, but what stops me as a malicious player from just sending "I just got item X" packages to the server directly? 🙂 Anyways, thats besides the original point of this thread. I don't know of any good articles on data driven game design in C#, but I do know one for Rust and I imagine the concept should translate very well
Pobiega
Pobiega•2mo ago
{
"name" : "Healing Potion",
"renderable": {
"glyph" : "!",
"fg" : "#FF00FF",
"bg" : "#000000"
},
"consumable" : {
"effects" : { "provides_healing" : "8" }
}
}
{
"name" : "Healing Potion",
"renderable": {
"glyph" : "!",
"fg" : "#FF00FF",
"bg" : "#000000"
},
"consumable" : {
"effects" : { "provides_healing" : "8" }
}
}
this is his example of a healing potion
GDcheerios
GDcheeriosOP•2mo ago
right, I just added authentication to the server, which hopefully will stop most of it, Cheat engine is still a problem. 😭 for now I can restrict users. Since all of it is online with data metrics it will be pretty easy to tell when someone starts cheating. I gotcha
Pobiega
Pobiega•2mo ago
Fair enough. You'll find that it will become a huge problem if the game ever gets any popularity, as cheating is unfortunately quite rampant if the game allows it in any way, be that exploitable networking or direct memory editing
GDcheerios
GDcheeriosOP•2mo ago
yes 😭 I think about it in the shower I honestly don't know how people will view my game it's a pretty simple concept alright, thanks for your advice and fix! Have a great one! 👋

Did you find this page helpful?