JSON manipulation - c#

I'm working on GitHub API and I want to manipulate the JSON response of this API so I have this API code
public class TrendRepo
{
public IEnumerable<object> Data;
}
[HttpGet]
public async Task<JsonResult> GetTrendingRepos()
{
var date = DateTime.Now.AddDays(-30).ToString("yyyy-MM-ddThh:mm:ssZ");
string trendingReposLanguagesUrl = #"https://api.github.com/search/repositories?q=created:>" + (date) + "&sort=stars&order=desc&per_page=10";
HttpWebRequest request = WebRequest.CreateHttp(trendingReposLanguagesUrl);
request.Accept = "application/json";
request.UserAgent = "request";
WebResponse response = await request.GetResponseAsync();
Stream data = response.GetResponseStream();
StreamReader reader = new StreamReader(data ?? throw new InvalidOperationException());
var readerResult = await reader.ReadToEndAsync();
//var jObj2 = JsonConvert.DeserializeObject<dynamic>(readerResult);
JToken token = JObject.Parse(readerResult);
var items = token["items"];
var arr = new List<object>();
List<dynamic> tripDetailsCollection = new List<dynamic>();
if (items != null)
{
foreach (dynamic o in items)
{
arr.Add(o.language);
arr.Add(o.id);
arr.Add(o.full_name);
arr.Add(o.html_url);
arr.Add(" ");
tripDetailsCollection.AddRange(arr);
}
}
TrendRepo trendRepo = new TrendRepo()
{
Data = arr,
};
return new JsonResult(trendRepo);
}
which return the response like this
{
"data": [
"Python",
319029846,
"beurtschipper/Depix",
"https://github.com/beurtschipper/Depix",
" ",
"C++",
311683390,
"WerWolv/ImHex",
"https://github.com/WerWolv/ImHex",
" ",
null,
316705066,
"peng-zhihui/PocketLCD",
"https://github.com/peng-zhihui/PocketLCD",
" "
]
}
but what I want is to be something like this
{
"data": [
"Python",
319029846,
"full_name":[
"beurtschipper/Depix",
"beurtschipper/Depix",
],
"https://github.com/beurtschipper/Depix",
" ",
]
"data": [
"C++",
311683390,
"full_name":[
"beurtschipper/Depix",
"WerWolv/ImHex",,
],
"https://github.com/WerWolv/ImHex",
" ",
]
"data": [
null,
316705066,
"full_name":[
"beurtschipper/Depix",
"WerWolv/ImHex",,
],
"https://github.com/peng-zhihui/PocketLCD",
" "
]
}
I tried to add another foreach within the existing one to kind of loop the
property but it gave me the same result.
also, I need to select a distinct language which is easy to do but
the trick move is I want all repos names and count which depend on this
language within the array, like in the JSON response I want above.
Any help I would be grateful.

#Hazeem, Spent bit time to see what we can do get the search results closer to your expectations, basically the way you defined JSON is useless no one would be able to parse, I don't think even serializer would accept for example data collection is not separated by commas, I tied up code a bit to work closer to what you want.
Code:
var tripDetailsCollection = new List<object>();
var date = DateTime.Now.AddDays(-30).ToString("yyyy-MM-ddThh:mm:ssZ");
string trendingReposLanguagesUrl = #"https://api.github.com/search/repositories?q=created:>" + (date) + "&sort=stars&order=desc&per_page=10";
var request = WebRequest.CreateHttp(trendingReposLanguagesUrl);
request.Accept = "application/json";
request.UserAgent = "request";
var response = await request.GetResponseAsync();
Stream data = response.GetResponseStream();
JToken token;
using (var reader = new StreamReader(data ?? throw new InvalidOperationException()))
{
var readerResult = await reader.ReadToEndAsync();
token = JObject.Parse(readerResult);
}
if (token != null)
{
var items = token["items"];
if (items != null)
{
foreach (dynamic o in items)
{
var arr = new
{
data = new List<object>
{
o.language,
o.id,
o.full_name,
o.html_url,
" "
}
};
tripDetailsCollection.Add(arr);
}
}
}
var json = JsonConvert.SerializeObject(tripDetailsCollection);
Result - you should be able loop through the collection and use it elsewhere.

What I need is to wrap my TrendRepo class with the Parent class with the same property values and DeserializeObject of the instead of here's the answer and feel free if anything not obvious.
public class Root
{
[JsonPropertyName("items")]
public List<Items> Items { get; set; }
}
public class Items //TrendRepo
{
[JsonPropertyName("language")]
public string Language { get; set; }
[JsonPropertyName("id")]
public int Id { get; set; }
[JsonPropertyName("name")]
public string Name { get; set; }
[JsonPropertyName("full_name")]
public string Full_Name { get; set; }
public string total_count { get; set; }
}
and edit the API like this
var readerResult = await reader.ReadToEndAsync();
Root jObj2 = JsonConvert.DeserializeObject<Root>(readerResult);
var result = jObj2.Items.Select(x => new
{
x.Language,
x.Id,
x.Full_Name,
x.Name
}).GroupBy(x => x.Language).ToArray();
return new JsonResult(result);

Related

getting 'System.Text.Json.JsonElement' does not contain 'result' Parsing with JsonSerializer.DeserializeAsync

I have JSON in a response structured like this
{
"statusCode": 200,
"result": {
"generalInfo": {
"firstName": "",
"lastName": ""
},
"isError": false
},
"sentDate": "2022-02-06T10:04:06.6853775Z",
}
var response = await _httpClient.PostAsJsonAsync(address, model);
Stream responseBody = await response.Content.ReadAsStreamAsync();
The response from the endpoint is actually a bit more complex and I want to extract only specific fields so I used dynamic
dynamic data = await JsonSerializer.DeserializeAsync<dynamic>(responseBody)
var dataString = data.ToString();
// dataString is
{"result":{"generalInfo":{"firstName":"John","lastName":"Doe"},"isError":false}
If I try to access dynamic properties like this
var firstName = data.result.generalInfo.firstName;
var lastName = data.result.generalInfo.lastName;
var isErr = data.IsError;
I'm getting exception
'System.Text.Json.JsonElement' does not contain a definition for
'result'
The problem is not DeserializeAsync, but that System.Text.Json uses JsonElements, so the dynamic value is actually a JsonElement. You'll need to parse the data from the JsonElement.
However, if you're using .Net 6 you can use JsonNode:
// deserialize into JsonNode
JsonNode data = await JsonSerializer.DeserializeAsync<JsonNode>(responseBody);
// OR parse the json string
JsonNode data = JsonNode.Parse(dataString);
int statusCode = (int)data["statusCode"];
var firstName = (string)data["result"]["generalInfo"]["firstName"];
var lastName = (string)data["result"]["generalInfo"]["lastName"];
var isErr = (bool)data["result"]["isError"];
Demo in .Net6
Support for dynamic was added to .Net 6 preview v4-6 but was removed before final release in favour of the JsonNode approach above. See github What's new in .NET 6 Preview 7 for further details.
Otherwise, you could parse it manually:
bool isError = false;
string firstName = "";
string lastName = "";
if (data is JsonElement elt && elt.TryGetProperty("result", out elt))
{
isError = elt.TryGetProperty("isError", out JsonElement boolElt) ? boolElt.GetBoolean() : false;
if (elt.TryGetProperty("generalInfo", out var genInfo))
{
firstName = genInfo.TryGetProperty("firstName", out var firstElt) ? firstElt.GetString() : "";
lastName = genInfo.TryGetProperty("lastName", out var lastElt) ? lastElt.GetString() : "";
}
}
However, my preferred approach is always to deserialize into defined classes if you can as it makes life much easier:
public class GeneralInfo
{
public string firstName { get; set; }
public string lastName { get; set; }
}
public class Result
{
public GeneralInfo generalInfo { get; set; }
public bool isError { get; set; }
}
public class Root
{
public Result result { get; set; }
}
var data = await JsonSerializer.DeserializeAsync<Root>(responseBody);
var firstName = data.result.generalInfo.firstName;

How can i get all values of JSON array

I have this JSON response:
{
"post_parameters_error_flag": false,
"data_error_flag": false,
"row_count": 7,
"message": "Operazione completata.",
"title": [
"title0",
"title1",
"title2",
"title3",
"title4",
"title5",
"title6"
],
"data": [
"value0",
"value1",
"value2",
"value3",
"value4",
"value5",
"value6",
"value7"
]
}
I need to write in a list (for example) all values obtained from 'data' array but I don't know how to get data from response:
string URL = "myurl";
string Params = string.Format("hwid={0}&building={1}", Utils.UUID(), "test");
Request HTTPRequest = new Request();
JObject JSONObject = JObject.Parse(await HTTPRequest.PostAsyncResponse(URL, Params));
//now? what can i do
Any idea to solve?
You can either use like this:
var result = await HTTPRequest.PostAsyncResponse(URL, Params)
var token = JToken.Parse(result);
var data= token.Value<JArray>("data");
Or you can also use JsonPath:
var result = await HTTPRequest.PostAsyncResponse(URL, Params)
var token = JToken.Parse(result);
var data = token.SelectTokens("$.data[*]");
But really, you should be serilizing into an object and then using the properties to get the data (or other properties):
public class RootObject
{
public bool post_parameters_error_flag { get; set; }
public bool data_error_flag { get; set; }
public int row_count { get; set; }
public string message { get; set; }
public List<string> title { get; set; }
public List<string> data { get; set; }
}
var result = await HTTPRequest.PostAsyncResponse(URL, Params)
var item = JsonConvert.DeserializeObject<RootObject>(result);
var data = item.data;
Try this,
var data = JObject.Parse(await HTTPRequest.PostAsyncResponse(URL, Params))["data"]
OR
var jsonObject = (JObject)JsonConvert.DeserializeObject(await HTTPRequest.PostAsyncResponse(URL, Params));
var data = (JObject)(jsonObject.Property("data").Value);

Return JSON as response from the Web API

I am trying to query the SQL server and trying to return the response in the JSON format. I am able to get the JSON response like
[
{"R_ID":"368203","ROOM":"K2"},
{"R_ID":"368203","ROOM":"K2"}
]
But I want the response to be wrapped inside the data like
{
"data": [
{"R_ID":"368203","ROOM":"K2"},
{"R_ID":"368203","ROOM":"K2"}
]
}
So now I changed my model class like
public class DatabaseResult
{
public int r_id { get; set; }
public string room { get; set; }
}
public class RootObject
{
public List<DatabaseResult> data { get; set; }
}
I am not sure how would I change the controller to get the response as expected
connection.Open();
List<DatabaseResult> records = new List<DatabaseResult>();
using (var reader = command.ExecuteReader())
{
while (reader.Read())
{
var row = new DatabaseResult
{
request_id = (int)reader["request_id"],
room = (string)reader["room"],
};
records.Add(row);
}
return Ok(records);
}
You could wrap records in an anonymous object
return Ok( new {data = records} );
You need to create the same Model in your backend, and populate like,
RootObject result = new RootObject();
while (reader.Read())
{
var row = new DatabaseResult
{
request_id = (int)reader["request_id"],
room = (string)reader["room"],
};
records.Add(row);
}
result.data = records;
then return the result.

Finding a JSON entry and add a nested element

I have a .json file that looks like this:
[
{
"username": "John",
"currency": 8,
"pulls":
[
{
"character": "person"
},
{
"character": "loved one"
}
]
},
{
"username": "Mike",
"currency": 2,
"pulls":
[
{
"character": "noone"
}
]
},
{
"username": "Clara",
"currency": 5,
"pulls":
[
{
"character": "someone"
}
]
}
]
What I managed to do so far is modify "currency":
bool userExists = false;
string jsonPointsString = File.ReadAllText(userPath);
dynamic jsonObjects = JsonConvert.DeserializeObject(jsonPointsString);
foreach (var jsonObject in jsonObjects)
{
if (jsonObject["username"] == user)
{
jsonObject["currency"] += value;
string output = JsonConvert.SerializeObject(jsonObjects, Formatting.Indented);
File.WriteAllText(userPath, output);
userExists = true;
}
}
As well as add a completely new entry from scratch:
JsonCollection.User user = new JsonCollection.User();
user.username = username;
user.currency = 10;
using (StreamReader r = new StreamReader(userPath))
{
string json = r.ReadToEnd();
List<JsonCollection.User> users = JsonConvert.DeserializeObject<List<JsonCollection.User>>(json);
users.Add(user);
newJson = JsonConvert.SerializeObject(users, Formatting.Indented);
}
File.WriteAllText(userPath, newJson);
However, no matter what I try I can not add another element to "pulls". The idea is that I call a function with a username and a pull, two strings. Based on the username variable I have to find the corresponding Json Entry and create a new entry within the "pulls" tree based on the pull variable. This is what I could come up with:
public void AddPullToUser(string user, string newPull)
{
user = "Mike"; //test value
string jsonPointsString = File.ReadAllText(userPath);
dynamic jsonObjects = JsonConvert.DeserializeObject(jsonPointsString);
foreach (var jsonObject in jsonObjects)
{
if (jsonObject["username"] == user)
{
//jsonObject["pulls"] = newPull;
JsonCollection.Character pull = new JsonCollection.Character();
pull.character = newPull;
jsonObject["pulls"] = pull;
string output = JsonConvert.SerializeObject(jsonObjects, Formatting.Indented);
File.WriteAllText(userPath, output);
}
}
}
If I do it like this the system can't convert the JsonCollection to the JArray but without using the JArray I don't understand how to find the specific users tree.
In step two this will have to be expanded even further to not create duplicated "pulls", but first of all this has to work in general.
Any help would be greatly appreciated.
Something like this -
var json = "[{'username':'John','currency':8,'pulls':[{'character':'person'},{'character':'loved one'}]},{'username':'Mike','currency':2,'pulls':[{'character':'noone'}]},{'username':'Clara','currency':5,'pulls':[{'character':'someone'}]}]";
var obj = JsonConvert.DeserializeObject<List<RootObject>>(json);
var o = obj.FindIndex(a => a.username == "Mike");
obj[o].pulls.AddRange(new List<Pull>{
new Pull{
character = "Modified"
}
});
Console.WriteLine(JsonConvert.SerializeObject(obj));
Where
public class Pull
{
public string character { get; set; }
}
public class RootObject
{
public string username { get; set; }
public int currency { get; set; }
public List<Pull> pulls { get; set; }
}
alternatively, you might be interested in JSON Merge
A possible solution looks like -
var json = "[{'username':'John','currency':8,'pulls':[{'character':'person'},{'character':'loved one'}]},{'username':'Mike','currency':2,'pulls':[{'character':'noone'}]},{'username':'Clara','currency':5,'pulls':[{'character':'someone'}]}]";
var obj = JArray.Parse(json);
var idx = obj.IndexOf(obj.FirstOrDefault(a => a["username"].ToString() == "Mike"));
((JArray)obj[idx]["pulls"]).Add(JObject.Parse(#"{
'character': 'new one'
}"));
Console.WriteLine(obj[idx]);
/*output -
{
"username": "Mike",
"currency": 2,
"pulls": [
{
"character": "noone"
},
{
"character": "new one"
}
]
} */
After a bit more research and your help I was able to first of all change all the interaction with Json to the same code-style.
New entry has changed to this:
public void CreateUser(string username)
{
try
{
string jsonUserString = File.ReadAllText(userPath);
var users = JsonConvert.DeserializeObject<List<JsonCollection.User>>(jsonUserString);
users.AddRange(new List<JsonCollection.User> { new JsonCollection.User { username = username, currency = 10, pulls = new List<JsonCollection.Character> { new JsonCollection.Character { character = "TemmieHYPE" } } } });
string output = JsonConvert.SerializeObject(users, Formatting.Indented);
File.WriteAllText(userPath, output);
}
catch
{
Console.WriteLine("Error on CreateUser");
}
}
Update has changed to this:
public void UpdateUserStats(string username, decimal value, int selection)
{
try
{
string jsonUserString = File.ReadAllText(userPath);
var users = JsonConvert.DeserializeObject<List<JsonCollection.User>>(jsonUserString);
int user = users.FindIndex(a => (a.username == username));
if (user != -1)
{
switch (selection)
{
case 1:
users[user].currency += value;
break;
case 2:
users[user].secondsOnline += value;
break;
default:
break;
}
string output = JsonConvert.SerializeObject(users, Formatting.Indented);
File.WriteAllText(userPath, output);
//AddPullToUser(username, DateTime.Now.ToString()); //remove on live
}
else
{
CreateUser(username);
}
}
catch
{
Console.WriteLine("Error on UpdateCurrency");
}
}
Most importantly the Add Pull command to add a nested element to the Json now works with the following code:
public void AddPullToUser(string username, string pulledCharacter)
{
string jsonUserString = File.ReadAllText(userPath);
var users = JsonConvert.DeserializeObject<List<JsonCollection.User>>(jsonUserString);
int alreadyPulled = users.FindIndex(a => (a.username == username) && (a.pulls.FindIndex(b => b.character == pulledCharacter) > 0));
if (alreadyPulled == -1)
{
int user = users.FindIndex(a => (a.username == username));
users[user].pulls.AddRange(new List<JsonCollection.Character> { new JsonCollection.Character { character = pulledCharacter } });
string output = JsonConvert.SerializeObject(users, Formatting.Indented);
File.WriteAllText(userPath, output);
}
}
With the addition of the "if (alreadyPulled == -1)" duplicated pulls don't get added to the Json files either.

Convert json response to list

I do a request to a api that I'm using and this is the response that I get back.
{ "id": 1139, "performanceStatus": "OK", "availabilityStatus": "OK" }
I would like to convert this response in a list where I then can use a for/foreach loop and later can use linq and SelectMany to create a whole new list.
I got this far, but I am stuck the code is not hitting the var "newJson"..
Can someone help me out?
var json = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
dynamic parsedJson = JObject.Parse(json);
foreach (var j in parsedJson)
{
j.Replace(JObject.FromObject(
new {
id = j.id,
performance = j.performanceStatus,
availability = j.availabilityStatus
}));
}
var newJson = parsedJson.ToString();
Later I would like to Deserialize it into a strongly typed class. Like so
boi = await Task.Run(() => JsonConvert.DeserializeObject<Boi>(newJson)).ConfigureAwait(false);
Here is the strongly typed class
public class Boi
{
public int Id { get; set; }
public string PerformanceStatus { get; set; }
public string AvailabilityStatus { get; set; }
}
public class NewBoi
{
public List<Boi> eeg { get; set; }
}
You could do this:
var jsn = "{ \"id\": 1139, \"performanceStatus\": \"OK\", \"availabilityStatus\": \"OK\" }";
var bois = new List<Boi> { JsonConvert.DeserializeObject<Boi>(jsn) };
var newBoi = new NewBoi() { eeg = bois };
Although I would question the need for the NewBoi class - you could just work with the List.
To deserialize to a well known type use
JsonConvert.Deserialize<T>(jsonString,new JsonSerializerSettings
{
ContractResolver = new CamelCasePropertyNamesContractResolver()
});
where T is your well known type.

Categories

Resources