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.
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 already put the access as being 'read/write' so everyone including me can read and write to the json file. However, the code is still sending out an UnauthorizedAccessException afterwards. This is the code:
public static void read_json(string path)
{
using (StreamReader r = new StreamReader("/Users/steve/Downloads/city_list.json"))
{
string json = r.ReadToEnd();
List<City_Data> items = JsonConvert.DeserializeObject<List<City_Data>>(json);
foreach (City_Data item in items)
{
var key = item.name;
var value = item.id;
dict.Add(key, value);
}
}
}
while this is part of the json file which I have been wanting to access and read from:
[
{
"id": 833,
"name": "Ḩeşār-e Sefīd",
"state": "",
"country": "IR",
"coord": {
"lon": 47.159401,
"lat": 34.330502
}
},
...
]
Make all files writeable when checking out of source control.
Call the Attrib MSBuild task before the transformation to remove the read-only file attribute.
For example:
<Attrib Files="/Users/steve/Downloads/city_list.json" Normal="true"/>
Call the Exec MSBuild task before the transformation to remove the read-only file attribute.
For example:
<Exec Command="attrib -R "/Users/steve/Downloads/city_list.json""/>
I have a problem, and i can't figure this out myself..
In my program i have an auto updater, when my program updates a new(changed, some new keys) config file is created. what i want my program to do is, is when it's updating to look at both config files(old and new) and transfer old settings that match a key in the new file to the new file.
This is an example of the old file:
{
"Setting1": false,
"Setting2": 123,
"Setting3": "test",
"LocationList": {
"Country": "NL",
"Locations": [
{
"Latitude": 38.556807486461118,
"Longitude": -121.2383794784546
},
{
"Latitude": -33.859019,
"Longitude": 151.213098
},
{
"Latitude": 47.5014969,
"Longitude": -122.0959568
},
{
"Latitude": 51.5025343,
"Longitude": -0.2055027
}
]
}
}
And this can be the new file(can also be different):
{
"Setting1": null,
"Setting2": null,
"Setting3": "",
"Setting4": ""
"LocationList": {
"Country": "",
"Locations": [
{
"Latitude": null,
"Longitude": null
},
{
"Latitude": null,
"Longitude": null
}
]
}
}
Expected result:
{
"Setting1": false,
"Setting2": 123,
"Setting3": "test",
"Setting4": ""
"LocationList": {
"Country": "NL",
"Locations": [
{
"Latitude": 38.556807486461118,
"Longitude": -121.2383794784546
},
{
"Latitude": -33.859019,
"Longitude": 151.213098
},
{
"Latitude": 47.5014969,
"Longitude": -122.0959568
},
{
"Latitude": 51.5025343,
"Longitude": -0.2055027
}
]
}
}
First, i looked at creating a class in c# and just deserialize it, then, i came to the conclusion that this is not possible because i don't know what the config is going to look like.
Second, i thought using a dynamic would do the trick, it didn't, because i didn't knew any keys that were in it. And couldn't figure out how to figure that out.
And lastly, i've looked if it would be possible using regex, for me, this seems impossible..
Can anybody give me some ideas of how they would do it? I don't need code, just a push in the right direction.
P.S. i do not want to combine the two, when there is a key in the old file but not in the new one, it doesn't need to be transferred(Only lists will be completely transferred from the old file, also when the list is empty/filled in the new one).
If you really want to try something in JSON, I can only recommend the excellent JSON.Net library to parse the json for you. Using LINQ to JSON you could easily find matching keys in a recursive fashion between the old config file and the newer one and simply copy the value from one to the other
see documentation and a small example at http://www.newtonsoft.com/json/help/html/LINQtoJSON.htm
briefly you could do so in pseudoCode. Obviously this would not be super performant because you would recursively walk two files a lot of times and could be optimized, but at the same time unless your configuration files are monstrous blobs this should not pose any problem on any kind of modern hardware
//load both config files
//load the first token in original file and walk recursively
//for each token try to match in the new file and write data if required using the same recursive technique to walk the other file
I don't need code, just a push in the right direction.
Store your configuration in a regular App.config file and leverage the System.Configuration. Depending on the configuration complexity you can either use the ready <appSettings> or construct your own config section(s) and elements. It still be easier and error proof than doing custom config.
The matter is too complicated to start inventing the wheel just to use another (text) format. Even if you solve your current problem, there will be more, which are already solved somewhere within the System.Configration...
You can start exploring the configuration options here; anyways a regular search in your favorite search engine will do the trick...
Oke after some trouble i've managed to fix it(Thanks #Louis).
This is how i've done it:
private static bool TransferConfig(string baseDir, ISession session)
{
//if (!session.LogicSettings.TransferConfigAndAuthOnUpdate)
// return false;
var configDir = Path.Combine(baseDir, "Config");
if (!Directory.Exists(configDir))
return false;
var oldConf = GetJObject(Path.Combine(configDir, "config.json.old"));
var oldAuth = GetJObject(Path.Combine(configDir, "auth.json.old"));
GlobalSettings.Load("");
var newConf = GetJObject(Path.Combine(configDir, "config.json"));
var newAuth = GetJObject(Path.Combine(configDir, "auth.json"));
TransferJSON(oldConf, newConf);
TransferJSON(oldAuth, newAuth);
File.WriteAllText(Path.Combine(configDir, "config.json"), newConf.ToString());
File.WriteAllText(Path.Combine(configDir, "auth.json"), newAuth.ToString());
return true;
}
private static JObject GetJObject(string filePath)
{
return JObject.Parse(File.ReadAllText(filePath));
}
private static bool TransferJSON(JObject oldFile, JObject newFile)
{
try
{
foreach (var newProperty in newFile.Properties())
foreach (var oldProperty in oldFile.Properties())
if (newProperty.Name.Equals(oldProperty.Name))
{
newFile[newProperty.Name] = oldProperty.Value;
break;
}
return true;
}
catch
{
return false;
}
}
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
I have been working on an application which allows the user to make a label template for printing purposes by adding label controls to a panel(which I use as a container). I have reached the point where I need to be able to save the template to a file which I can load into memory later for printing. Since the form is not serializable does anyone have suggestions on how I can save the form or container(with added label controls) to a file that can be reused later?
Thanks.
I wouldn't directly serialize a form to a file. Sounds like you need to create a class that will hold the state of the user's work. You should then serialize that class to and from a file. There are built in methods for that using either Binary or XML Serialization.
Create a struct that contains enough information (and no more) about each Label that you can reconstitute the Label from it.
Write a method that takes a List<MyStruct> and populates a Panel from your structs.
Write methods to serialize and deserialize this list.
Encapsulate the whole thing in a class.
Try this. It uses the ISerializationSurrogate interface to get around the problem of the form object not being serializable:
How to serialize an object which is NOT marked as 'Serializable' using a surrogate.
http://www.codeproject.com/KB/dotnet/Surrogate_Serialization.aspx
Personally, I would serialize it as JSON.
When bringing it back you can use a generic method
that loops through and sets the properties through reflection.
Also take notice that the library I've linked to will automatically serialize objects that you pass to it.
JSON
JSON.NET
[{ "Label": [{"Top": 102}, {"Left": 105}, {"Text": "blah, blah"}] }]
From JSON.NET
Product product = new Product();
product.Name = "Apple";
product.Expiry = new DateTime(2008, 12, 28);
product.Price = 3.99M;
product.Sizes = new string[] { "Small", "Medium", "Large" };
string json = JsonConvert.SerializeObject(product);
//{
// "Name": "Apple",
// "Expiry": new Date(1230422400000),
// "Price": 3.99,
// "Sizes": [
// "Small",
// "Medium",
// "Large"
// ]
//}
Product deserializedProduct = JsonConvert.DeserializeObject<Product>(json);
You can get the position, size and other properties about the form's controls at runtime and save that state in an XML or JSON file.
This isn't trivial, but personally I would set up a function that can be called recursively that would add nodes to an XML file.
I don't have actual code, but pseudo-code looks like this: (you will need to do some clean-up, because I'm doing this off the top of my head without the aid of Intellisense.)
XmlDocument doc;
function SaveForm()
{
doc = new XmlDocument("FormInfo");
foreach(Control ctrl in this.Controls)
{
AddControlToXml(ctrl, doc.Documentelement);
}
}
function AddControlToXml(Control ctrl, XmlNode currentNode)
{
XmlNode n = new XmlNode;
Node.InnerText = ctrl.Name;
foreach(Control ctrl2 in ctrl.Controls)
{
AddControlToXml(ctrl2);
}
}