56

I have a settings.json file present in the Release folder of my application. What I want to do is change the value of it, not temporarily, permanently.. That means, deleting the old entry, writing a new one and saving it.

Here is the format of the JSON file

{
"Admins":["234567"],
"ApiKey":"Text",
"mainLog": "syslog.log",
"UseSeparateProcesses": "false",
"AutoStartAllBots": "true",
"Bots": [
    {
        "Username":"BOT USERNAME",
        "Password":"BOT PASSWORD",
        "DisplayName":"TestBot",
        "Backpack":"",
        "ChatResponse":"Hi there bro",
        "logFile": "TestBot.log",
        "BotControlClass": "Text",
        "MaximumTradeTime":180,
        "MaximumActionGap":30,
        "DisplayNamePrefix":"[AutomatedBot] ",
        "TradePollingInterval":800,
        "LogLevel":"Success",
        "AutoStart": "true"
    }
]
}

Suppose I want to change the password value and instead of BOT PASSWORD I want it to be only password. How do I do that?

John Saunders
  • 160,644
  • 26
  • 247
  • 397
Bone
  • 859
  • 2
  • 9
  • 17

3 Answers3

96

Here's a simple & cheap way to do it (assuming .NET 4.0 and up):

string json = File.ReadAllText("settings.json");
dynamic jsonObj = Newtonsoft.Json.JsonConvert.DeserializeObject(json);
jsonObj["Bots"][0]["Password"] = "new password";
string output = Newtonsoft.Json.JsonConvert.SerializeObject(jsonObj, Newtonsoft.Json.Formatting.Indented);
File.WriteAllText("settings.json", output);

The use of dynamic lets you index right into json objects and arrays very simply. However, you do lose out on compile-time checking. For quick-and-dirty it's really nice but for production code you'd probably want the fully fleshed-out classes as per @gitesh.tyagi's solution.

agentnega
  • 3,478
  • 1
  • 25
  • 31
  • it worked though the indentation got messed up.. it's all in 1 line.. any way to fix that?? – Bone Feb 11 '14 at 07:19
  • Yes, `JsonConvert.SerializeObject` can take a 2nd parameter, `Formatting.Indented`. Edited answer. – agentnega Feb 11 '14 at 07:27
  • I searched for this everywhere.. Thanks. Just one question.. Why I can't use var instead of dynamic when declaring jsonObj? – Alexander Sep 20 '18 at 19:10
  • 2
    @Alexander: because the return type of DeserializeObject is plain old `object`. If the content of the json was a simple dictionary (no nested arrays or sub-objects) you could use `var jsonObj = JsonConvert.DeserializeObject>(json);` and get a Dictionary back. Alternatively, if you only need to pick out certain values, see [this answer](https://stackoverflow.com/a/4749755/426028) for another approach. – agentnega Sep 24 '18 at 17:04
  • `JsonConvert` removes the "$type" attribute. `JObject.Parse` does not. – Rachel Martin Oct 18 '18 at 19:37
  • Is there any way to delete a node using this method? – monofal May 13 '19 at 11:02
28

Use the JObject class in Newtonsoft.Json.Linq to modify JSON values without knowing the JSON structure ahead of time:

using Newtonsoft.Json.Linq;

string jsonString = File.ReadAllText("myfile.json");
// Convert the JSON string to a JObject:
JObject jObject = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonString) as JObject;
// Select a nested property using a single string:
JToken jToken = jObject.SelectToken("Bots[0].Password");
// Update the value of the property: 
jToken.Replace("myNewPassword123");
// Convert the JObject back to a string:
string updatedJsonString = jObject.ToString();
File.WriteAllText("myfile.json", updatedJsonString);

Example:

// This is the JSON string from the question
string jsonString = "{\"Admins\":[\"234567\"],\"ApiKey\":\"Text\",\"mainLog\":\"syslog.log\",\"UseSeparateProcesses\":\"false\",\"AutoStartAllBots\":\"true\",\"Bots\":[{\"Username\":\"BOT USERNAME\",\"Password\":\"BOT PASSWORD\",\"DisplayName\":\"TestBot\",\"Backpack\":\"\",\"ChatResponse\":\"Hi there bro\",\"logFile\":\"TestBot.log\",\"BotControlClass\":\"Text\",\"MaximumTradeTime\":180,\"MaximumActionGap\":30,\"DisplayNamePrefix\":\"[AutomatedBot] \",\"TradePollingInterval\":800,\"LogLevel\":\"Success\",\"AutoStart\":\"true\"}]}";

JObject jObject = Newtonsoft.Json.JsonConvert.DeserializeObject(jsonString) as JObject;

// Update a string value:
JToken jToken = jObject.SelectToken("Bots[0].Password");
jToken.Replace("myNewPassword123"); 

// Update an integer value:
JToken jToken2 = jObject.SelectToken("Bots[0].TradePollingInterval");
jToken2.Replace(555);

// Update a boolean value:
JToken jToken3 = jObject.SelectToken("Bots[0].AutoStart");
jToken3.Replace(false);

// Get an indented/formatted string:
string updatedJsonString = jObject.ToString(); 

//Output:
//{
//  "Admins": [
//    "234567"
//  ],
//  "ApiKey": "Text",
//  "mainLog": "syslog.log",
//  "UseSeparateProcesses": "false",
//  "AutoStartAllBots": "true",
//  "Bots": [
//    {
//      "Username": "BOT USERNAME",
//      "Password": "password",
//      "DisplayName": "TestBot",
//      "Backpack": "",
//      "ChatResponse": "Hi there bro",
//      "logFile": "TestBot.log",
//      "BotControlClass": "Text",
//      "MaximumTradeTime": 180,
//      "MaximumActionGap": 30,
//      "DisplayNamePrefix": "[AutomatedBot] ",
//      "TradePollingInterval": 555,
//      "LogLevel": "Success",
//      "AutoStart": false
//    }
//  ]
//}
Community
  • 1
  • 1
Josh Withee
  • 9,922
  • 3
  • 44
  • 62
18

You must have classes to instantiate json values to :

public class Bot
    {
        public string Username { get; set; }
        public string Password { get; set; }
        public string DisplayName { get; set; }
        public string Backpack { get; set; }
        public string ChatResponse { get; set; }
        public string logFile { get; set; }
        public string BotControlClass { get; set; }
        public int MaximumTradeTime { get; set; }
        public int MaximumActionGap { get; set; }
        public string DisplayNamePrefix { get; set; }
        public int TradePollingInterval { get; set; }
        public string LogLevel { get; set; }
        public string AutoStart { get; set; }
    }
    
 

   public class RootObject
    {
        public List<string> Admins { get; set; }
        public string ApiKey { get; set; }
        public string mainLog { get; set; }
        public string UseSeparateProcesses { get; set; }
        public string AutoStartAllBots { get; set; }
        public List<Bot> Bots { get; set; }
    }

Answer to your Ques(Untested code) :

//Read file to string
string json = File.ReadAllText("PATH TO settings.json");

//Deserialize from file to object:
var rootObject = new RootObject();
JsonConvert.PopulateObject(json, rootObject);

//Change Value
rootObject.Bots[0].Password = "password";

// serialize JSON directly to a file again
using (StreamWriter file = File.CreateText(@"PATH TO settings.json"))
{
    JsonSerializer serializer = new JsonSerializer();
   serializer.Serialize(file, rootObject);
}
obachtos
  • 977
  • 1
  • 12
  • 30
gitesh.tyagi
  • 2,271
  • 1
  • 14
  • 21