C
C#16mo ago
Kye

Update JSON File with new values without overwriting existing data

Hello, So I'm currently developing a Discord bot that stores values such as 'settings' for each guild. I already got that part figured out very well but I would like to know how I should handle additions / removes. I have a class as followed:
public class GuildConfiguration
{
public string GuildPrefix { get; set; } = default!;
public bool EnableModeratorNotifications { get; set; } = default!;
public GuildCommandPermissions GuildCommandPermissions { get; set; } = default!;
}
public class GuildConfiguration
{
public string GuildPrefix { get; set; } = default!;
public bool EnableModeratorNotifications { get; set; } = default!;
public GuildCommandPermissions GuildCommandPermissions { get; set; } = default!;
}
As an example, currently, the json file doesn't have the public bool EnableModeratorNotifications included but it is a new feature I would like to add. If I add that to my class, how would I serialize it into the json file with a default value like true/false without overwriting the existing values there?
46 Replies
jcotton42
jcotton4216mo ago
are you deserializing the JSON into an instance of that before writing it back out?
Kye
Kye16mo ago
Yes.
jcotton42
jcotton4216mo ago
I think system.text.json will just use the default value then (btw, you don't need ! on that bool, that's only for nullable ref types)
Kye
Kye16mo ago
ah okay
jcotton42
jcotton4216mo ago
also, if you want those props to always be set, you might want
public required string GuildPrefix { get; set; }
public required string GuildPrefix { get; set; }
instead
Kye
Kye16mo ago
What does 'required' specifically do?
jcotton42
jcotton4216mo ago
the compiler yells at you if you try to create an instance without setting that prop
MODiX
MODiX16mo ago
jcotton42#1663
REPL Result: Failure
class GuildSettings {
public required string GuildPrefix { get; set; }
}
var settings = new GuildSettings();
class GuildSettings {
public required string GuildPrefix { get; set; }
}
var settings = new GuildSettings();
Exception: CompilationErrorException
- Required member 'GuildSettings.GuildPrefix' must be set in the object initializer or attribute constructor.
- Required member 'GuildSettings.GuildPrefix' must be set in the object initializer or attribute constructor.
Compile: 489.636ms | Execution: 0.000ms | React with ❌ to remove this embed.
Kye
Kye16mo ago
ah okay makes sense
MODiX
MODiX16mo ago
jcotton42#1663
REPL Result: Success
class GuildSettings {
public required string GuildPrefix { get; set; }
}
var settings = new GuildSettings() { GuildPrefix = "foo" };
class GuildSettings {
public required string GuildPrefix { get; set; }
}
var settings = new GuildSettings() { GuildPrefix = "foo" };
Compile: 553.507ms | Execution: 56.267ms | React with ❌ to remove this embed.
Kye
Kye16mo ago
So, I heard about [JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)] and DefaultValue() would that do something? or could I basically do public string GuildPrefix { get; set; } = "Value Here";
jcotton42
jcotton4216mo ago
that looks like Newtonsoft? are you using newtonsoft, or system.text.json?
Kye
Kye16mo ago
I think it is Newtonsoft if I am not mistaken Yep, it's Newtonsoft.Json;
jcotton42
jcotton4216mo ago
I'm not familiar with that part of it I always use STJ
Kye
Kye16mo ago
Is there a benefit of using STJ over Newtonsoft?
jcotton42
jcotton4216mo ago
faster, lower memory usage, etc. Newtonsoft has more features, but I don't find myself needing them it's also one less dependency to bundle, if you care about that
Kye
Kye16mo ago
oh, i did hear about STJ but I somehow always ended up using Newtonsoft.Json I will try your advice and check
jcotton42
jcotton4216mo ago
in any case, here's how STJ works by default
JsonConvert.DeserializeObject<GuildConfiguration>("""
{
"GuildPrefix": "foo",
"GuildCommandPermissions": 3
}
""").Dump();

public class GuildConfiguration
{
public string GuildPrefix { get; set; }
public bool EnableModeratorNotifications { get; set; } = true;
public int GuildCommandPermissions { get; set; }
}
JsonConvert.DeserializeObject<GuildConfiguration>("""
{
"GuildPrefix": "foo",
"GuildCommandPermissions": 3
}
""").Dump();

public class GuildConfiguration
{
public string GuildPrefix { get; set; }
public bool EnableModeratorNotifications { get; set; } = true;
public int GuildCommandPermissions { get; set; }
}
jcotton42
jcotton4216mo ago
gives
Kye
Kye16mo ago
Oh, so it serializes on it's own basically?
jcotton42
jcotton4216mo ago
err, oops meant NewtonSoft pretty sure STJ is the same, lemme try
Kye
Kye16mo ago
ohh
jcotton42
jcotton4216mo ago
oh huh
jcotton42
jcotton4216mo ago
I bet that's configurable...
Kye
Kye16mo ago
Hm I see
jcotton42
jcotton4216mo ago
yeah that's intended
Kye
Kye16mo ago
For some reason, for me .Dump(); isn't available
jcotton42
jcotton4216mo ago
ah sorry that's a LINQPad thing it's writes to the lower pane
Kye
Kye16mo ago
oh okay Instead of a string, is it possible to deserialize the object using a path to the json file? Because if so, I would simply add it to my loop when checking if each guild has the appropriate json file if it exists, simply update it
jcotton42
jcotton4216mo ago
STJ can take a stream to deserialize it looks like NewtonSoft can't, which is surprising so you'd need to read the file into memory as a string then deserialize
Kye
Kye16mo ago
So like this,
string guildJson = File.ReadAllText($"app1000/guilds/{getGuild.Id}/guilddata.json");
JsonConvert.DeserializeObject<GuildInformation>(guildJson);
string guildJson = File.ReadAllText($"app1000/guilds/{getGuild.Id}/guilddata.json");
JsonConvert.DeserializeObject<GuildInformation>(guildJson);
jcotton42
jcotton4216mo ago
yeah
Kye
Kye16mo ago
I did but for some reason the new value wasn't added to the json
jcotton42
jcotton4216mo ago
well, you're only deserializing you're not serializing back to it (also, if you're not that far in, you might want to consider using a database; managing tons of json files will get hairy pretty fast)
Kye
Kye16mo ago
oop i was told that json can be pretty tough but i get it, im deserializing it but i have to serialize it back
jcotton42
jcotton4216mo ago
it's not "json is tough" it's "when you have a ton of files spread about you're poorly approximating a database"
Kye
Kye16mo ago
I would use a database since I have worked with MySQL before a lot
jcotton42
jcotton4216mo ago
for a bot I'd use something like SQLite or PostgreSQL (I have... opinions about MySQL, tbh) EF Core makes interfacing with the db pretty easy
Kye
Kye16mo ago
The bot would handle a lot of cases at once and im not sure how the performance would be for the bot like, i once added a leveling system which means everytime a user types a message, it would change his score in the json which worked fine
jcotton42
jcotton4216mo ago
databases are obscenely fast even sqlite
Kye
Kye16mo ago
Would that mean I have to keep the connection to the database open 24/7 once it's open hm i see
jcotton42
jcotton4216mo ago
if you use EF it manages the connection for you I haven't done it manually in a long while
Kye
Kye16mo ago
i haven't worked with a DB in awhile yeah I still can't seem to figure out how to update the json file Setting a default value in the designer class works fine but actually adding that value into the json file doesn'twork :/ Nevermind, it worked using this code:
var getGuildDataPath = File.ReadAllText($"app1000/guilds/{getGuild.Id}/guilddata.json");
var getGuildData = JsonConvert.DeserializeObject<GuildInformation>(getGuildDataPath);

var updateGuildFile = JsonConvert.SerializeObject(getGuildData, Formatting.Indented);
File.WriteAllText($"app1000/guilds/{getGuild.Id}/guilddata.json", updateGuildFile);
var getGuildDataPath = File.ReadAllText($"app1000/guilds/{getGuild.Id}/guilddata.json");
var getGuildData = JsonConvert.DeserializeObject<GuildInformation>(getGuildDataPath);

var updateGuildFile = JsonConvert.SerializeObject(getGuildData, Formatting.Indented);
File.WriteAllText($"app1000/guilds/{getGuild.Id}/guilddata.json", updateGuildFile);
I mean this looks kinda weird but it works?
jcotton42
jcotton4216mo ago
I mean how else would you expect it to work? you deserialize to an object update the object serialize back
Kye
Kye16mo ago
Well, tbh, this looks like sloppy work. I usually look at the code in a more clean way but this will do.