简体   繁体   中英

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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM