How to Parse Json to obtains values - c#

This is the Json Format from the Imgur API using gallery search (heavily simplified, what matters is still there)
{
"data":[
{
"id":"q33FYFh",
"is_album":true,
"images":[
{
"id":"ObcYQRc",
"link":"https:\/\/i.imgur.com\/ObcYQRc.jpg",
"is_album":false
},
{
"id":"ifB0uac",
"link":"https:\/\/i.imgur.com\/ifB0uac.jpg",
"is_album":false
}
]
},
{
"id":"jYInL3c",
"is_album":true,
"images":[
{
"id":"bq2L5C4",
"link":"https:\/\/i.imgur.com\/bq2L5C4.jpg",
"is_album":false
},
{
"id":"Z0OPngk",
"link":"https:\/\/i.imgur.com\/Z0OPngk.jpg",
"is_album":false
}
]
},
{
"id":"8xxM5TO",
"link":"https:\/\/i.imgur.com\/8xxM5TO.jpg",
"is_album":false
}
],
"success":true,
"status":200
}
I need a way to get all image ID, not album ID, you can tell if an item is an album or a image by looking at the "is_album" tag
So first I tried to at least access the "id" subfield in the "images" field :
using Newtonsoft.Json;
string response = "Change this with the json file above"
dynamic dynJson = JsonConvert.DeserializeObject(response);
foreach (var data in dynJson)
{
string id = data["images"]["id"].ToString();
Debug.WriteLine(id);
}
This gave me this error : (By the way, I need to use Debug.WriteLine because Console doesn't work in PCL code in Xamarin.Forms)
System.InvalidOperationException: Cannot access child value on Newtonsoft.Json.Linq.JProperty.
But even if it worked, it would not get the id's for the images that are not part of an album.
using pseudo code this is what I would want (I think) :
for each (item in myjson)
{
if (item.is_album == "false")
{
Console.write(item.id);
}
else
{
for each (image in element)
{
Console.write(image.id);
}
}
}

You are not accessing your data correctly based on your JSON data.
data is your top level array, so your foreach would look like this:
foreach (var data in dynJson["data"])
Inside your foreach you would access your images like this:
string id = data["images"][0]["id"].ToString();
Where 0 is the index of the images array.
So combining this with another loop to get all the images for that data:
foreach (var data in dynJson["data"])
{
if (data["is_album"] == false)
{
// continue or do something
continue;
}
foreach(var image in data["images"])
{
string id = image["id"].ToString();
Debug.WriteLine(id);
}
}

You are accessing the dynamic object incorrectly. Here is the code you need:
public static List<string> GetImageIds(string jsonData)
{
List<string> imageIds = new List<string> ();
dynamic temp = JsonConvert.DeserializeObject (jsonData);
dynamic dynJson = temp.data;
foreach (dynamic data in dynJson)
{
int j = 0;
if (data.is_album == false)
{
imageIds.Add (data.id.ToString ());
}
else
{
dynamic images = data.images;
foreach (var image in images)
{
imageIds.Add (image.id.ToString ());
}
}
}
return imageIds;
}
Note: This is a simple example of how to traverse dynamic object. You will need to add validations and business logic to it as needed per your need.
Hope this helps!

Related

Newton-soft - How to add values to nested attributes which not even exists

I have below Json structure
{
"Name": "abc",
"Grade": "a"
}
Now, i want to add values to the attributes which is location in hierarchy. For example, i want to add value just like below -
Name.Lavel-1.Lavel-2.Lavel-3.Direaction = "East"
As above, i have to add value of "Direction" attribute, which itself is located inside Lavel-3 attribute which does not exists. Same way, Lavel-1 and Lavel-2 does not even exist at the time of addition.
So, my requirement is to add the required hierarchy and then add the value. So, after the addition, the json should look like below
{
"Name": "Jack",
"Grade": "A",
"Lavel-1": {
"Lavel-2": {
"Lavel-3": {
"Direction": "East"
}
}
}
}
I google and tried some solutions, but most of them are simple adding/Updating the values to an existing path,i.e, where the hierarchy is already available,and the modification is done only of the Leaf node.
Need help, if we can achieve this with efficiency.
One way is to add new JObjects and JProperties:
var jstring= #"{
""Name"": ""abc"",
""Grade"": ""a""
}";
var json = JObject.Parse(jstring);
json.Add(
new JProperty("Lavel-1",
new JObject(new JProperty("Lavel-2",
new JObject(new JProperty("Lavel-3",
new JObject(new JProperty("Direction","East"))
))
))
)
);
Thank you Magnetron for the response.
updating answer which helps me to get my needs as it can help other dev as well.
I found the below steps on another stackoverflow thread which satisfy my need. I change a little bit. Below code block will add the path ( if not exists) and set the value.
private void AddPropertyToJTokenWithValue(JToken jtoken, string tokenPath, string value)
{
if (jtoken == null || tokenPath == null)
{
return;
}
var pathParts = tokenPath.Split('.');
foreach (var pathPart in pathParts)
{
var partNode = jtoken.SelectToken(pathPart);
if (partNode == null)
{
try
{
if (pathPart != pathParts.Last())
{
((JObject)jtoken).Add(pathPart, new JObject());
partNode = jtoken.SelectToken(pathPart);
}
else
{
((JObject)jtoken).Add(pathPart, value);
partNode = jtoken.SelectToken(pathPart);
}
}
catch (Exception ex)
{
// log
return;
}
}
jtoken = partNode;
}
return;
}

UWP: MediaPlaybackItem get ViewModel

My MediaPlaybackList.ShuffledItems has 10 items in it. But when I was trying to convert the items back to a list of ViewModel (in my case it is Music) using the uri, I got null.
Why is that? Is it because I load the file from my local drive?
This is how I get the uri:
public static async Task<List<Music>> GetRealPlayList()
{
if (PlayList.ShuffleEnabled)
{
if (ShuffledPlayList.Count == 0)
{
foreach (var music in PlayList.ShuffledItems)
{
ShuffledPlayList.Add(await Music.GetMusic(music.Source.Uri.AbsolutePath));
}
}
return ShuffledPlayList;
}
else
return CurrentPlayList;
}
This is how I set the items:
public static async Task SetPlayList(IEnumerable<Music> playlist)
{
if (Helper.SamePlayList(playlist, CurrentPlayList)) return;
PlayList.Items.Clear();
CurrentPlayList.Clear();
foreach (var music in playlist)
{
var item = new MediaPlaybackItem(MediaSource.CreateFromStorageFile(await Helper.CurrentFolder.GetFileAsync(music.GetShortPath())));
PlayList.Items.Add(item);
CurrentPlayList.Add(music);
}
}
What ways else can I convert the MediaPlackBackItem back to the ViewModel? The GetDisplayProperties().MusicProperties doesn't have the some properties that I want and the properties in it are also empty.
When you create MediaSource,you can set CustomProperties to save the file path in it.And when you loop through the PlayList.ShuffledItems,get file path from the CustomProperties.
Set the items:
MediaSource source = MediaSource.CreateFromStorageFile(await Helper.CurrentFolder.GetFileAsync(music.GetShortPath()));
source.CustomProperties.Add("Path", file.Path);
var item = new MediaPlaybackItem(source);
Get Music class:
foreach (var music in PlayList.ShuffledItems)
{​
MediaSource source = music.Source;​
String path = sour.CustomProperties["Path"].ToString();​
ShuffledPlayList.Add(await Music.GetMusic(path));​
}

Request.Files is empty when files come through as an array

I'm doing some integration with a third party form builder software that allows the form to be posted to our own server. The form data and files are then saved to a DB. The issue is when the form contains multiple file upload fields, Request.Files is always empty.
Using Fiddler, I can see the binary files coming through. The only thing that I can think of is that the field name contains brackets in them (because it's being sent as an array) and so the model binder can't bind it properly? The field names that are coming through are tfa_20[0] and tfa_20[1].
Code-wise, it's pretty standard stuff:
var data = new Submission()
{
ConfigurationDetailId = configDetail.Id,
SubmitterEmail = submitterEmail,
SubmissionData = Request.Form.AllKeys.Select(k => new SubmissionData()
{
FieldName = k,
FieldValue = Request.Form[k]
}).ToList(),
SubmissionFiles = new List<SubmissionFile>()
};
// process any files uploaded
if (Request.Files.Count > 0)
{
foreach (string field in Request.Files)
{
var uploadedFile = Request.Files[field];
if (!string.IsNullOrEmpty(fileName))
{
data.SubmissionFiles.Add(GetSubmissionFile(uploadedFile, fileName));
}
}
}
Repository.SaveForm(data);
Any help would greatly be appreciated.
Use HttpPostedFileBase in order to get posted file to your action. in case of multiple files,should be used an HttpPostedFileBase[] array.
To Enable uploading in forms, is necessary to add enctype="multipart/form-data" to your form tag. or if you use razor syntax change your beginForm tag to this.
View.cshtml
#using (Html.BeginForm("action","controller", FormMethod.Post, new { #enctype =
"multipart/form-data" }))
{
}
public ActionResult YourAction(HttpPostedFileBase[] files)
{
var data = new Submission()
{
ConfigurationDetailId = configDetail.Id,
SubmitterEmail = submitterEmail,
SubmissionData = Request.Form.AllKeys.Select(k => new SubmissionData()
{
FieldName = k,
FieldValue = Request.Form[k]
}).ToList(),
SubmissionFiles = new List<SubmissionFile>()
};
if (files.Length > 0)
{
foreach (HttpPostedFileBase file in files)
{
var uploadedFile = file;
if (!string.IsNullOrEmpty(file.FileName))
{
data.SubmissionFiles.Add(GetSubmissionFile(uploadedFile, file.fileName));
}
}
}
return View();
}

JSON Parsing issue with C# and RestSharp

I am parsing the following JSON:
{
result: [
{
EventType: {
EventTypeDesc: "horse-racing",
events: {
Local: {
Events: {
530857: {
Venue: "Northam",
StateCode: "WA",
CountryCode: "AUS"
},
530858: {
Venue: "Caulfield",
StateCode: "VIC",
CountryCode: "AUS"
}
}
}
}
}
}
]
}
I can access the element through following code:
responseDeserializeObject.result[0].EventType.events.Local.Events["530857"].Venue
However, the following C# code doesn't work:
dynamic responseDeserializeObject = HttpGetResponse(endPoint);
foreach (var event in responseDeserializeObject.result[0].EventType.events.Local.Events)
{
Console.WriteLine(event.Venue);
Console.WriteLine(event.StateCode);
Console.WriteLine(event.CountryCode);
}
Any help will be highly appreciated.
I think your Events is a dictionary, so you need to get KeyValuePair in the foreach loop and access it's Value property. And also change the scoped variable name event, it will not compile, event is a reserved word.
dynamic responseDeserializeObject = HttpGetResponse(endPoint);
foreach (var ev in responseDeserializeObject.result[0].EventType.events.Local.Events)
{
var value = ev.Value;
Console.WriteLine(value.Venue);
Console.WriteLine(value.StateCode);
Console.WriteLine(value.CountryCode);
}

replacing a variable text with other text in c#

I am using c# and sitecore to basically use tokens in certain places ( see: how to create a custom token in sitecore ). I think I have a solution, but am not sure as to why it is not working, even though I am getting no errors.
Item tokenItem = Sitecore.Context.Database.Items["/sitecore/content/Site Content/Tokens"];
if (tokenItem.HasChildren)
{
var sValue = args.FieldValue.ToString();
foreach (Item child in tokenItem.Children)
{
if (child.Template.Name == "Token")
{
string home = child.Fields["Title"].Value;
string hContent = child.Fields["Content"].Value;
if (sValue.Contains(home))
{
home.Replace(home, hContent);
}
}
}
}
home and hContent pull up the correct values of each container, but when the page loads, it still has the "home" value inputted (the ie: ##sales) in the content area instead of the new value, which is stored in hContent. The sValue contains everything (tables, divs, text) and I was trying to single out a value that equals to "home" and replace the "home" value with hContent. What am I missing?
If your code is implemented as a processor for the RenderField pipeline, you need to put the result of your work back into args. Try something like this:
Item tokenItem = Sitecore.Context.Database.Items["/sitecore/content/Site Content/Tokens"];
if (tokenItem.HasChildren)
{
var sValue = args.Result.FirstPart;
foreach (Item child in tokenItem.Children){
if (child.Template.Name == "Token") {
string home = child.Fields["Title"].Value;
string hContent = child.Fields["Content"].Value;
if (sValue.Contains(home)) {
sValue = sValue.Replace(home, hContent);
}
}
}
args.Result.FirstPart = sValue;
}
Note that you need to be sure to patch this processor into the pipeline after the GetFieldValue processor. That processor is responsible for pulling the field value into args.Result.FirstPart.
You code isn't really doing anything. You seem to be replacing the tokens on the token item field itself (child.Fields["Title"] and child.Fields["Content"]), not on the output content stream.
Try the following, you need to set the args to the replaced value, replacing both the FirstPart and LastPart properties: Replace Tokens in Rich Text Fields Using the Sitecore ASP.NET CMS (link to the code in the "untested prototype" link).
I would refactor your code to make it easier:
public void Process(RenderFieldArgs args)
{
args.Result.FirstPart = this.Replace(args.Result.FirstPart);
args.Result.LastPart = this.Replace(args.Result.LastPart);
}
protected string Replace(string input)
{
Item tokenItem = Sitecore.Context.Database.Items["/sitecore/content/Site Content/Tokens"];
if (tokenItem.HasChildren)
{
foreach (Item child in tokenItem.Children)
{
if (child.Template.Name == "Token")
{
string home = child.Fields["Title"].Value;
string hContent = child.Fields["Content"].Value;
if (input.Contains(home))
{
return input.Replace(home, hContent);
}
}
}
}
return input;
}
This is still not optimal, but gets you closer.
Well, Do you know what happens when you performs home.Replace(home, hContent);, it will create a new instance by replacing the content of the come with what is in hContent so what you need to do is, assign this instance to a new variable or to home itself. hence the snippet will be like the following:
if (sValue.Contains(home))
{
home = home.Replace(home, hContent);
}
Have you tried:
home = home.Replace(home,hContent);

Categories

Resources