I have a couple JObjects that are being returned from different places but that have all the same properties. I need to concatenate/merge this into one larger jObject. Is this possible and how would I go about doing it?
I want it to have all the same proerties as the individual objects. For instance.
jObject1 = { "data": [{"name": "foo","id": "1234" }]};
jObject2 = {"data": [{ "name": "foo2", "id": "5678" }]};
Resulting in something like this.
jobject3 = { "data": [{ "name": "foo", "id": "1234"}, { "name": "foo2", "id": "5678" }]};
I'm coding in C# and the only thing I have thought about doing so far is something like this which isn't valid. Not really sure how to begin and can't really anything.
jobject3 = jObject1.Concat(jObject2);
I am trying to manually loop through each object and build a new object. I think I am close but keep getting an error when adding the second item (oAlldepartment.Add) saying "Can not add property to Newtonsoft.Json.Linq.JObject. Property with the same name already exists on object.".
dynamic dynObj = JsonConvert.DeserializeObject(people);
foreach (var item in dynObj.data)
{
string id = item.id;
string name = item.name;
department = getdepartment(id);
JObject oDepartment = new JObject();
try
{
if (!String.IsNullOrEmpty(department))
oDepartment = JObject.Parse(department);
}
catch (Exception ex)
{
}
JArray departmentArray = new JArray();
if (oDepartment != null)
{
foreach (var x in oDepartment["data"].Children())
{
try
{
JObject departmentObject = new JObject();
((JObject)departmentObject).Add(new JProperty("name", x["name"]));
((JObject)departmentObject).Add(new JProperty("department", new JObject(new JProperty("name", x["department"]["name"]))));
((JObject)departmentObject).Add(new JProperty("hire_date", x["hire_date"]));
((JObject)departmentObject).Add(new JProperty("description", x["description"]));
departmentArray.Add(departmentObject);
}
catch (Exception ex)
{
}
((JObject)x).Add(new JProperty("itemtype", "post"));
}
try
{
oAlldepartment.Add(new JProperty("", new JArray(departmentArray)));
}
catch (Exception ex)
{
}
}
}
Thanks,
Rhonda
What I ended up doing was creating a class that defined the json I wanted to return and adding the json properties from each indivual object. The other benefit is my data returned by the method to the client is cleaner as I only have to worry about the properties I need instead of a huge json object with a bunch of properties that I don't need.
Rhonda
Related
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;
}
I saw plenty of similar questions but I really could not work my way around this time. What I am having issues with, is populating a view with the content of a certain object in order to update it. When I press on "Edit", in theory, all the fields should be automatically inserted. What have I tried so far?
public ActionResult CreateOrEdit(int id = 0)
{
if (id==0)
return View(new Recipe());
else
{
HttpResponseMessage response = GlobalVariables.client.GetAsync(id.ToString()).Result;
//return ViewJsonConvert.DeserializeObject<IList<Recipe>>(response.ToString()));
return View(response.Content.ReadAsAsync<Recipe>().Result);
//return View(new Recipe());
}
}
return View(response.Content.ReadAsAsync<Recipe>().Result); - when using this as a return, I am getting this error:
Cannot deserialize the current JSON array (e.g. [1,2,3])
After looking the problem up, I tried the following:
return View(JsonConvert.DeserializeObject<IList<Recipe>>(response.ToString())); which got me this error:
{"Unexpected character encountered while parsing value: s. Path '', line 0, position 0."}
And at this point I am stuck. I would assume that it is trying to deserialize the following JSON:
{
"id": 5002,
"name": "Test Recipe",
"recipeLink": "testlink",
"category1Id": 7757,
"category2Id": 7758,
"category3Id": 7759,
"category4Id": 7760,
"recipeById": 1,
"totalTime": 30,
"totalTimeUnitId": 1,
"activeTime": 20,
"activeTimeUnitId": 1,
"instructions": "Test Instructions",
"sourceKey": "Test SK",
"recipeBy": "TestPerson",
"insertedAtUtc": "2019-09-04T12:18:48.0466667",
"isVerified": 1,
"numPersons": 5
}
If needed, here is the code from the API Controller that is handling the operations.
[Route("v1/recipe/{id}")]
[HttpPut()]
public IActionResult UpdateList(int id, [FromBody]Recipe recipe)
{
var category1Id = 7757;
var category2Id = 7758;
var category3Id = 7759;
var category4Id = 7760;
var isVerified = 0;
var recipeBy = "TestPerson";
var recipeById = 1;
try
{
if (recipe == null) throw new ArgumentException("No data specified");
//if (newData.Name == null) throw new ArgumentException("No name specified");
using (var con = _connFactory())
{
con.Execute(#"UPDATE dbo.Recipe SET Name=#name, RecipeLink=#recipeLink, Category1Id=#category1Id ,Category2Id=#category2Id,
Category3Id=#category3Id, Category4Id=#category4Id, RecipeById=#recipeById, TotalTime=#totalTime, TotalTimeUnitId=#totalTimeUnitId,
ActiveTime=#activeTime, ActiveTimeUnitId=#activeTimeUnitId, Instructions=#instructions, SourceKey=#sourceKey, RecipeBy=#recipeBy,
InsertedAtUtc=getutcdate(), IsVerified=#isVerified, NumPersons=#numPersons WHERE Id=#id",
new
{
id,
recipe.name,
recipe.recipeLink,
category1Id,
category2Id,
category3Id,
category4Id,
recipeById,
recipe.totalTime,
recipe.totalTimeUnitId,
recipe.activeTime,
recipe.activeTimeUnitId,
recipe.instructions,
recipe.sourceKey,
recipeBy,
isVerified,
recipe.numPersons
});
}
return Ok(recipe);
}
catch (Exception exc)
{
return BadRequest();
}
}
I believe your response string starts with the Unicode Byte Order Mark character.
For example...
using Newtonsoft.Json;
namespace StackOverflow
{
class MainClass
{
public static void Main(string[] args)
{
var json = #"{
'id': 5002,
'name': 'Test Recipe',
'recipeLink': 'testlink',
'category1Id': 7757,
'category2Id': 7758,
'category3Id': 7759,
'category4Id': 7760,
'recipeById': 1,
'totalTime': 30,
'totalTimeUnitId': 1,
'activeTime': 20,
'activeTimeUnitId': 1,
'instructions': 'Test Instructions',
'sourceKey': 'Test SK',
'recipeBy': 'TestPerson',
'insertedAtUtc': '2019-09-04T12:18:48.0466667',
'isVerified': 1,
'numPersons': 5
}".Replace("'", "\"");
//This works...
var deserialized1 = JsonConvert.DeserializeObject(json);
//Prepend a U+FEFF Byte Order Mark character...
json = "\uFEFF" + json;
//This fails with error:
//Newtonsoft.Json.JsonReaderException:
//Unexpected character encountered while parsing value: . Path '', line 0, position 0.
var deserialized2 = JsonConvert.DeserializeObject(json);
}
}
}
I wonder if you have a string value where it is expecting an integer, i.e. a character which is not wrapped in quotation marks.
Could you debug to the line JsonConvert.DeserializeObject<IList<Recipe>>(response.ToString())); in Visual Studio and then inspect the contents of "response" in the locals window and post a screenshot to help diagnose the issue?
Edit in response to comment:
Try adding these steps in before the return, then see what is contained in the temp variable:
string temp = response.Content.ReadAsAsync<string>().Result;
It will be interesting to see what this holds, maybe then it will show why the data cannot be parsed into a Recipe object.
Further edit
OK, instead of that line that won't execute try adding this code:
Stream receiveStream = response.GetResponseStream();
StreamReader readStream = new StreamReader(receiveStream, Encoding.UTF8);
var temporaryData = readStream.ReadToEnd();
See if that will execute and then inspect the contents of tempoararyData variable.
Edit 3
OK great we can see the data now. I believe the issue is it is coming through as a JSON array and you're trying to deserialize a singular version. I made this demo app to illustrate:
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
string data = "[{\"Id\":5002,\"Name\":\"Test Recipe\"}]";
string data2 = "{\"Id\":5002,\"Name\":\"Test Recipe\"}";
//This throws an exception
//DataClass account = JsonConvert.DeserializeObject<DataClass>(data);
//This works
DataClass account2 = JsonConvert.DeserializeObject<DataClass>(data2);
//This also works
DataClass[] account3 = JsonConvert.DeserializeObject<DataClass[]>(data);
}
}
class DataClass
{
public int Id { get; set; }
public string Name { get; set; }
}
}
I believe if you change your code to deserialize an array i.e. DeserializeObject<IList<Recipe[]>> hopefully it will work
I am quite the beginner when it comes to programming but I am creating a POS for my WooCommerce website with the .net wrapper. So far everything that I want to do is going well except for one thing.
I am trying to create a new order and pass the products in but I don't know how to add multiple products through the parameters. This is my code:
public static async Task Transaction(int[] productId)
{
try
{
//TODO: Check if product is in stock
Dictionary<string,string> parameters = new Dictionary<string, string>();
parameters["payment_method"] = "cash";
parameters["set_paid"] = "true";
parameters["line_items"] = ??
await wc.Order.Add(new Order(), new Dictionary<string, string>(parameters));
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
}
I am not sure how to add an array to this ["line_items"] parameter or if I even could/should do this.
In PHP it looks like this:
$data = [
'payment_method' => 'cash',
'set_paid' => true,
'line_items' => [
[
'product_id' => 93,
'quantity' => 2
]
]
];
$woocommerce->post('orders', $data)
I would really appreciate it if anyone here can point me in the right direction. Thanks.
Instead of passing a dictionary as your parameters, it seems the Order object has a property "line_items", which is of the object "List"
So try something like this:
public static async Task Transaction(int[] productId)
{
try
{
//TODO: Check if product is in stock
Dictionary<string,string> parameters = new Dictionary<string, string>();
parameters["payment_method"] = "cash";
parameters["set_paid"] = "true";
parameters["line_items"] = ??
var lineItems = new List<OrderLineItem>();
lineItems.Add(new OrderLineItem());
await wc.Order.Add(new Order(){ line_items = lineItems }, parameters);
}
catch(Exception ex)
{
MessageBox.Show(ex.Message);
return;
}
}
IntelliSense should be able to help you from there :)
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!
I have searched high and low, and far and wide for a solution to this, and have spent the last few weeks trying to implement my own solution, but I just can't come up with anything.
I would greatly appreciate any help at all!
I have a file, which looks like, (file.json):
{
"Expense": {
"Name": "OneTel Mobile Bill",
"Amount": "39.90",
"Due": "28/12/2011",
"Recurrence": "1 Months",
"Paid": "0",
"LastPaid": "01/01/2002"
}
}
And in my app, when I create a new 'Expense', I want to append that new Expense to this existing JSON file, so it looks like so:
{
"Expense": {
"Name": "OneTel Mobile Bill",
"Amount": "39.90",
"Due": "28/12/2011",
"Recurrence": "1 Months",
"Paid": "0",
"LastPaid": "01/01/2002"
},
"Expense": {
"Name": "Loan Repayment",
"Amount": "50.00",
"Due": "08/03/2012",
"Recurrence": "3 Months",
"Paid": "0",
"LastPaid": "08/12/2011"
}
}
And this is how I am creating the JSON and writing to the file:
async public void WriteToFile(string type, string data)
{
file = await folder.GetFileAsync(file.FileName);
IRandomAccessStream writestream = await file.OpenAsync(FileAccessMode.ReadWrite);
IOutputStream outputstream = writestream.GetOutputStreamAt(0);
DataWriter datawriter = new DataWriter(outputstream);
datawriter.WriteString(data);
await datawriter.StoreAsync();
outputstream.FlushAsync().Start();
}
private void CreateExpenseButton_Click(object sender, RoutedEventArgs e)
{
//Create the Json file and save it with WriteToFile();
JObject jobject =
new JObject(
new JProperty("Expense",
new JObject(
new JProperty("Name", NameTextBox.Text),
new JProperty("Amount", AmountTextBox.Text),
new JProperty("Due", DueTextBox.Text),
new JProperty("Recurrence", EveryTextBox.Text + " " + EveryComboBox.SelectionBoxItem),
new JProperty("Paid", "0"),
new JProperty("LastPaid", "Never")
)
)
);
try
{
WriteToFile(Expenses, jobject.ToString());
// Close the flyout now.
this.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
}
catch (Exception exception)
{
Debug.Write(exception.Message);
}
}
I am using the Json.NET library from James Newton King, and it's pretty awesome but even after reading the included documentation, I have absolutely no idea how to read the JSON file and append data to it.
Are there any samples around that demonstrate how this is done, or can you recommend another library for C# that would allow me to accomplish this?
Edit
This is how I am reading a single expense from the json file:
JObject json = JObject.Parse(data);
Expense expense = new Expense
{
Amount = (string)json["Expense"]["Amount"],
Due = (string)json["Expense"]["Due"],
Name = (string)json["Expense"]["Name"]
};
Debug.Write(expense.Amount);
You can try to deserialize the data in to a object Expence and add your data, then serialize the object (list of objects) to file.
Since you can read directly into an Expense object, you should be able to add such an object to a List<Expense> - add the new data as an Expense object to the list (directly from your form data, or otherwise).
At this point you should be able to write out the List<Expense> out using JSON.NET - it should take care of creating the list.
I suggest you always save a List<Expense>, even if it contains only one item as it would make serializing and deserializing easier.