0

I'd like to be able to get/set the property values of a:

{
   "payload":[
      {
         "a":"yes",
         "b":"no",
         "c":"maybe"
      },
      {
         "a1":"agg",
         "b":"no",
         "c":"maybe"
      },
      {
         "a":"L",
         "b":"k",
         "c":"maybe"
      }
   ]
}

I'd like to be iterate through all of the a and get/set its values. In pseudocode something like this:

foreach(var jsonObject in payload)
{
  jsonObject.a = "ZZZZZZZZ";
}

The resulting structure would be something like this:

{
   "payload":[
      {
         "a":"ZZZZZZZZ",
         "b":"no",
         "c":"maybe"
      },
      {
         "a1":"agg",
         "b":"no",
         "c":"maybe"
      },
      {
         "a":"ZZZZZZZZ",
         "b":"k",
         "c":"maybe"
      }
   ]
}

How do we access values within objects within arrays?

Please note that the schema is dynamic.

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
Alex Gordon
  • 57,446
  • 287
  • 670
  • 1,062
  • Take a look at the calls within `System.Reflection`. You'll need to _reflect_ over each object (by calling `GetType()` on the object), get the object's fields and, if a field is named `a` and has the type `string` set its value to "ZZZZZZ" – Flydog57 Apr 04 '19 at 14:41
  • this is not something available in json.net? – Alex Gordon Apr 04 '19 at 14:42
  • yes, indexer operator (in JObject class) – Selvin Apr 04 '19 at 14:44

3 Answers3

3

There are a variety of options using JSON.NET. The one I'd probably go with based on my understanding of your use case is something like this:

var jObject = JsonConvert.DeserializeObject<JObject>(json);
foreach (var jsonObject in jObject["payload"])
{
    if (jsonObject["a"] != null)
    {
        jsonObject["a"] = "ZZZZZZZZ";
    }
}

One main advantage of this approach is that the JObject can be converted back into JSON (for example, if the purpose of your code is simply to make this change and save the JSON back again). Here's a LINQPad script showing this working.

Other options include:

  • Deserializing to a dynamic, so you can say obj.payload instead of obj["payload"]
  • Deserializing to a static type (which it sounds like is a non-starter for you since the schema is dynamic)
  • Using SelectTokens() to provide a JsonPath to the elements you want to change. This can be useful if the target objects are found deep within your JSON payload rather than at the top level.
StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
  • thanks so much, unrelated question, is there a different way to express `JsonConvert.DeserializeObject(json);` ? can we simply do `JsonConvert.DeserializeObject(json);` ? why do we need to specify `JObject`? – Alex Gordon Apr 04 '19 at 15:16
  • @RistoM: Good catch. I fixed my answer to accommodate that. – StriplingWarrior Apr 04 '19 at 15:20
  • @l--''''''---------'''''''''''': The DeserializeObject method was designed to be used for a variety of purposes. You can use it to deserialize to `dynamic` or `ExpandoObject` or to a static type of your choosing. There's no overload in the library that assumes you want that specific type. If you find yourself doing this often you could write your own utility method. – StriplingWarrior Apr 04 '19 at 15:24
  • got it! thank you very much for the linqpad script! runs great. from what i understand, it's not possible to use `SelectTokens` in a way that will allow us to reference `jsonObject["a"]` ? – Alex Gordon Apr 04 '19 at 15:29
  • 1
    @l--''''''---------'''''''''''' Yes it is possible. See [my answer](https://stackoverflow.com/a/55520540/10263) for an example. – Brian Rogers Apr 04 '19 at 16:09
2

As an example of the third bullet from @StriplingWarrior's answer:

You could use SelectTokens() with a JsonPath wildcard expression to find all the matching properties in the JSON and then modify those.

JToken root = JToken.Parse(json);
foreach (JToken match in root.SelectTokens("payload[*].a"))
{
    match.Replace(new JValue("ZZZZZZZZ"));
}

Fiddle: https://dotnetfiddle.net/ZYIchL

Brian Rogers
  • 125,747
  • 31
  • 299
  • 300
  • since you're doing a `.Replace()` on a `JToken`, does that mean we cannot do this: `match.Replace(new JArray(new JValue("ZZZZZZZZ")))`?? can we only put 'primitive' values within a `JToken` ? – Alex Gordon Apr 04 '19 at 16:40
  • 1
    `Replace` accepts a `JToken`, which could be a `JArray`, a `JObject` or a `JValue` (`JToken` is the base class for all of these). So [yes](https://dotnetfiddle.net/4Rrp7v), you can do `match.Replace(new JArray(new JValue("ZZZZZZZZ")))`. – Brian Rogers Apr 04 '19 at 16:54
  • See [this answer](https://stackoverflow.com/a/38560188/10263) for more information about how `JTokens` relate. – Brian Rogers Apr 04 '19 at 16:59
  • Thanks for adding this, Brian. – StriplingWarrior Apr 04 '19 at 19:47
1

Here is one solution with dto classes and Json.NET deserialisation/serialization. This solution has assumption that json schema is fixed.

    using Newtonsoft.Json;
    using System.Collections.Generic;
    class Program
    {
        static void Main(string[] args)
        {
            var json = @"{
               ""payload"":[
                  {
                     ""a"":""yes"",
                     ""b"":""no"",
                     ""c"":""maybe""
                  },
                  {
                     ""a1"":""agg"",
                     ""b"":""no"",
                     ""c"":""maybe""
                  },
                  {
                     ""a"":""L"",
                     ""b"":""k"",
                     ""c"":""maybe""
                  }
               ]
            }";

            var o= JsonConvert.DeserializeObject<RootObject>(json);
            foreach (var item in o.Payload)
            {
                item.A = "ZZZZZZZZ";
            }
            var convertedJson = JsonConvert.SerializeObject(o);


        }
    }

    public class RootObject
    {
        [JsonProperty("payload")]
        public List<Payload> Payload { get; set; }
    }

    public class Payload
    {
        [JsonProperty("a", NullValueHandling = NullValueHandling.Ignore)]
        public string A { get; set; }

        [JsonProperty("b")]
        public string B { get; set; }

        [JsonProperty("c")]
        public string C { get; set; }

        [JsonProperty("a1", NullValueHandling = NullValueHandling.Ignore)]
        public string A1 { get; set; }
    }
Risto M
  • 2,919
  • 1
  • 14
  • 27
  • thanks so much this is a great solution, unfortunately one important piece of info i forgot to share is that the schema is dynamic – Alex Gordon Apr 04 '19 at 15:06
  • Ok I see.. :) Then generic JObject or C# dynamic is the answer. If there is no other correct answer coming, I'll amend my answer later.. – Risto M Apr 04 '19 at 15:18