How remove element for json file in C# - c#

I have json file Users.json and I want delete one element. This is the structure:
{
"1": {
"Username": "1",
"Name": "1",
"AccessGroup": "Administrators"
},
"2": {
"Username": "2",
"Name": "2",
"AccessGroup": "Supervisors"
},
"3": {
"Username": "3",
"Name": "3",
"AccessGroup": "Administrators"
}
}
And the code:
public void DeleteUser(Users.User user)
{
String filename = USERS_PATH;
try
{
if (File.Exists(filename))
{
var data = DeserializeFromFile<Dictionary<String, User>>(USERS_PATH);
foreach (var item in data)
{
if (user.Username == data[item.Key].Username)
{
data.Remove(user.Username);
break;
}
}
}
}
catch (Exception)
{
throw new ArgumentException("User has not deleted");
}
}
After that iteration the file is the same like before. Where do I wrong? Thanks in advance.

There are a few things wrong with this code:
After loading the file into an object, the Dictionary<string,User> no longer corresponds to the file: if the dictionary is updated, the file isn't and vice versa, you you need to serialize the data again and save it to the file with:
String encoded = JsonConvert.SerializeObject(data);
System.IO.File.WriteAllText(filename,encode);
Dictionary.Remove asks to provide a key, you can remove with the username, it's not guaranteed (especially not in your example file), that the key is the username. The result is that nothing is removed. So you should use:
data.Remove(item.Key);
instead of:
data.Remove(user.Username);
Next, you shouldn't remove data in an iterator. Although this works in this case because you do a break, in general it's a very bad idea to do this since most enumerators are not designed to enumerate over changing collections. In this case you can store a reference to the key to be removed:
var torem = null;
foreach (var item in data) {
if (user.Username == data[item.Key].Username) {
torem = item.Key;
break;
}
}
if(torem != null) {
data.Remove(torem);
}
You can also save some CPU cycles by using item.Value instead of data[item.Key].Username since what you do is a lookup for a value where you already have the pointer so use:
if(user.Username == item.Value.Username)
instead of:
if (user.Username == data[item.Key].Username)

You have to save the data to he same file.
File.WriteAllText(USERS_PATH, JsonConvert.SerializeObject(data));
// I don't know what JSON parser you're using, but replace it with
// whatever that library provides for serializing data/saving to file :)
or something like this :)
Basically you're working on an in-memory copy of what was read from file. If you want to use a JSON like a DB, you might want to write some kind of DAO/Repository that will just expose basic CRUD methods (open file -> perform changes -> write file).

I could be wrong, but don't you have to delete an item by its key, not by a property of the item?
So:
data.Remove(item.Key);

Related

How do I call a specific piece of json data in a foreach loop?

(sorry for the bad title)
I have this json file
[
{
"userId": 3017221209,
"displayName": "Frank"
},
{
"userId": 1690049096,
"displayName": "dumb"
}
]
And I deserialized it with this
var userss = System.Text.Json.JsonSerializer.Deserialize<List<User>>(json);
But I tried to mention it in a foreach loop (like for each user id do this and that) but I couldn't figure it out
foreach (var usersstuff in userss) { /* other stuff you are not supposed to see */ }
So.. any help?
You can't use User like a variable if it's name's class, try this:
foreach (var user in userss) { /* other stuff you are not supposed to see */ }

Downloading configuration data

I have an application attached to the configuration file:
{
"ProjectModules": [
{
"Version": "1",
"LoginModule": {
"LoginLogic": "Project1.ModulesV1.LoginModule.Logic.LoginLogic"
}
},
{
"Version": "2",
"LoginModule": {
"LoginLogic": "Project1.ModulesV2.LoginModule.Logic.LoginLogic"
}
}
]
}
How to get the value for the "LoginLogic" key and for a specific version?
Here I started to do but it does not take into account that it is a data table
if (_configuration.GetSection("ProjectModules:" + moduleName).Exists())
{
var configSection = _configuration.GetSection("ProjectModules:" + moduleName);
if (configSection[sectionName] != null)
{
part = configSection[sectionName];
}
}
EDIT:
moduleName -> LoginModule
sectionName -> LoginLogic
I need to get the value for the "LoginLogic" key knowing the version "Version"
It's going to be extremely difficult, if not impossible, to do what you want with JSON formatted this way. You need to understand how the configuration system works. No matter what the config source (JSON, environment variables, console arguments, etc.) everything, and I mean everything ends up dumped into a dictionary. Pretty much the entire responsibility of a config provider is to take the source and convert it into a dictionary, which is then returned and merged into the main configuration dictionary.
As such, what you're actually creating here is:
["ProjectModules[0]:Version"] = 1
["ProjectModules[0]:LoginModule:LoginLogic"] = "Project1.ModulesV1.LoginModule.Logic.LoginLogic"
["ProjectModules[1]:Version"] = 2
["ProjectModules[1]:LoginModule:LoginLogic"] = "Project1.ModulesV2.LoginModule.Logic.LoginLogic"
As you can see, there's no real way here to tell exactly which version belongs to which LoginLogic, except for the index of ProjectModules being the same. However, since that's just a string serving as a key in the dictionary, it's not something you can easily filter or search on.
One option would be to change the format a bit if you can. For example, if you instead had JSON like:
{
"ProjectModules": {
"Version1": {
"LoginModule": {
"LoginLogic": "Project1.ModulesV1.LoginModule.Logic.LoginLogic"
}
},
"Version2": {
"LoginModule": {
"LoginLogic": "Project1.ModulesV1.LoginModule.Logic.LoginLogic"
}
}
}
}
Then, you'd end up with:
["ProjectModules:Version1:LoginModule:LoginLogic"] = "Project1.ModulesV1.LoginModule.Logic.LoginLogic"
["ProjectModules:Version2:LoginModule:LoginLogic"] = "Project1.ModulesV2.LoginModule.Logic.LoginLogic"
And, it's easy enough to distinguish then by version.

Parsing/Iterating over Json

I'm fairly new to parsing Json with C# and i'm having a little issue i can't work my head around.
My data looks something like this:
{
"languages": {
"ja_lang": {
"data": {
"name": "Japanese"
},
"files": [["ja",
"Japanese File",
"lang_ja.txt"]]
},
"en_lang": {
"data": {
"name": "English"
},
"files": [["en",
"English File",
"lang_en.txt"]]
}
}
}
Now i want to iterate over the items in languages and only work with the one where the object-name starts with "ja_" (in this case it would only work with "ja_lang" and ignore "en_lang"), then extract the name inside data and the "lang_ja.txt" in files.
To Parse the Json in C# i downloaded the Newtonsoft.Json library and came up with this:
dynamic json_obj = JsonConvert.DeserializeObject("json string");
// when debugging language holds { "ja_lang": { "data": { "name": "Japanese" }, "files": [["ja", "Japanese File", "lang_ja.txt"]] } }
foreach (var language in json_obj.languages)
{
// not sure how i can access the object-name
/*if(!language.StartsWith("ja_"))
continue;*/
// Exception: 'Newtonsoft.Json.Linq.JProperty' does not contain a definition for 'data' - Not sure why it is treated as a property?
var name = language.data.name;
var file = language.files[2];
}
I'm sorry for this probably dumb question, but i've been trying to cast it to different types and searched the web for solutions, but i just couldn't figure it out. So if someone could help me out with this i would be really greatful.
Thanks in advance!
Since you're stating in a comment (on an answer that has been deleted) that the data changes so a fixed model won't work, you can still fix what is known:
Here's a LINQPad program that demonstrates:
void Main()
{
var collection = JsonConvert.DeserializeObject<LanguagesCollection>(File.ReadAllText(#"c:\temp\test.json"));
foreach (var keyValuePair in collection.Languages)
if (keyValuePair.Key.StartsWith("ja_"))
keyValuePair.Value.Dump();
}
public class LanguagesCollection
{
public Dictionary<string, JObject> Languages { get; } = new Dictionary<string, JObject>();
}
This will deserialize the outer object, with the "languages" key, and inside you have a dictionary with the keys, "ja_lang", "en_lang", and you can just process the values as you see fit. These are left as JObject which means they will contain whatever json was present as a value for that key in the dictionary.
Using a site like json2sharp you can just pass your json data in and get a ready to use c# model out.
Then you can easily deserialize your json data into that c# model and use the properties for much easier handling:
string jsonData = #"{
'languages': {
'ja_lang': {
'data': {
'name': 'Japanese'
},
'files': [['ja',
'Japanese File',
'lang_ja.txt']]
},
'en_lang': {
'data': {
'name': 'English'
},
'files': [['en',
'English File',
'lang_en.txt']]
}
}
}";
RootObject data = JsonConvert.DeserializeObject<RootObject>(jsonData);
foreach(Languages lang in data.languages) //would work if Languages was a listing
{
}
Although I admit that your Json is a bit strange and that Languages most likly should be a listing and not a property for each language.

json foreach loop (without json.net)

I'm currently trying to create a small launcher to solve some problems using the existing launcher from minecraft.
I'm trying to read a .json file to get all the informations that i need.
If you need to take a look at the .json file here.
I got it working if i just need a single information like
string clienturl = readJson("//downloads/client/url");
with this:
private string readJson(string element)
{
string json = File.ReadAllText(Path.Combine(appPath + "1.10.2.json"));
var jsonReader = JsonReaderWriterFactory.CreateJsonReader(Encoding.UTF8.GetBytes(json), new System.Xml.XmlDictionaryReaderQuotas());
var root = XElement.Load(jsonReader);
return root.XPathSelectElement(element).Value;
}
The problem now is that i need to get informations for all the other files.
The "element" would be:
libraries/downloads/artifact/path
libraries/downloads/artifact/url
but obviously there is more then one entry for "path" and "url" so i need a foreach loop.
What do i need to change in my code above to make it working with a foreach loop?
Sorry for my bad english, i hope its not to hard to understand.
Small preview of the .json in case you dont want to download the file:
"libraries": [
{
"name": "com.mojang:netty:1.6",
"downloads": {
"artifact": {
"size": 7877,
"sha1": "4b75825a06139752bd800d9e29c5fd55b8b1b1e4",
"path": "com/mojang/netty/1.6/netty-1.6.jar",
"url": "https://libraries.minecraft.net/com/mojang/netty/1.6/netty-1.6.jar"
}
}
},
{
"name": "oshi-project:oshi-core:1.1",
"downloads": {
"artifact": {
"size": 30973,
"sha1": "9ddf7b048a8d701be231c0f4f95fd986198fd2d8",
"path": "oshi-project/oshi-core/1.1/oshi-core-1.1.jar",
"url": "https://libraries.minecraft.net/oshi-project/oshi-core/1.1/oshi-core-1.1.jar"
}
}
},
{
"name": "net.java.dev.jna:jna:3.4.0",
"downloads": {
"artifact": {
"size": 1008730,
"sha1": "803ff252fedbd395baffd43b37341dc4a150a554",
"path": "net/java/dev/jna/jna/3.4.0/jna-3.4.0.jar",
"url": "https://libraries.minecraft.net/net/java/dev/jna/jna/3.4.0/jna-3.4.0.jar"
}
}
}
]
The issue is that your JSON contains an array, and it's not immediately obvious how JsonReaderWriterFactory maps an array to XML elements, given that XML doesn't have the concept of an array.
One way to determine this is to read through the documentation Mapping Between JSON and XML which describes this mapping. But another is simply to determine for ourselves, by using one of the answers from Get the XPath to an XElement? to find out the actual paths for each element. Using this answer, the following code:
var paths = root.DescendantsAndSelf().Select(e => e.GetAbsoluteXPath()).ToList();
Debug.WriteLine(String.Join("\n", paths));
Produces output like:
/root/libraries
/root/libraries/item[1]
/root/libraries/item[1]/name
/root/libraries/item[1]/downloads
/root/libraries/item[1]/downloads/artifact
/root/libraries/item[1]/downloads/artifact/size
/root/libraries/item[1]/downloads/artifact/sha1
/root/libraries/item[1]/downloads/artifact/path
/root/libraries/item[1]/downloads/artifact/url
/root/libraries/item[2]
/root/libraries/item[2]/name
/root/libraries/item[2]/downloads
/root/libraries/item[2]/downloads/artifact
/root/libraries/item[2]/downloads/artifact/size
/root/libraries/item[2]/downloads/artifact/sha1
/root/libraries/item[2]/downloads/artifact/path
/root/libraries/item[2]/downloads/artifact/url
So, as you can see, each array item is placed in a synthetic <item> node.
Thus you can query your paths and urls as follows:
var files = root
.XPathSelectElements("libraries/item/downloads/artifact")
.Select(e => new PathAndUrl { Path = (string)e.Element("path"), Url = (string)e.Element("url") })
.ToList();
Placing the result into a list of the following class:
public class PathAndUrl
{
public string Path { get; set; }
public string Url { get; set; }
}

JSON.net Serialize With Namespaces

If I have json that looks something like this: (wrote this by hand, so it may have errors)
{
"http://devserver.somesite.com/someendpoint/1/2/3$metadata#Something.Start": [
{
"Title": "start",
"Endpoint": "https://devserver.somesite.com/someendpoint/1/2/3/start"
}
],
"http://devserver.somesite.com/someendpoint/1/2/3$metadata#Something.Stop": [
{
"Title": "stop",
"Endpoint": "https:// devserver.somesite.com/someendpoint/1/2/3/stop"
}
]
}
Is there any easy, built in way (JSON.net) to have it understand that there’s a namespace in play here? Or is there a way to set a variable or pattern based JsonProperty via an attribute?
I can't have the URL as part of my business object, because that will change from environment to environment.
I know I can create a custom json converter, but before going down that route I’d like to see if there’s something more out of box that handles this. Another option is to get the data via xml and handle that by hand.
Assuming you are taking this as a string that you have received from a web call you can do the following in JSON.NET.
var json = "your string here";
var obj = JObject.Parse(json);
foreach(var ns in obj.Properties) {
var arr = (JArray)ns.Value;
foreach(var obj2 in arr) {
// do you logic here to get the properties
}
}
Another option that James Newton-King provided you can do this, which seems a little cleaner:
var list = JsonConvert.DeserializeObject<Dictionary<string, List<MyClass>>>(json);

Categories