skip to Main Content

I am trying to find a more elegant solution than foreach loops for the following problem.

Sample JSON Structure:

[{
  "OpportunityId": "ABC-123-XYZ",
  "Status": "Active",
  "Milestones": {
    "Milestone": [
      {
        "Name": "Award",
        "Date": "8/27/2021"
      }
    ]
  },
  "Staff": {
    "Pocs": [
      {
        "Role": "Development Lead",
        "Name": "Doe, John",
        "Email": "[email protected]"
      },
      {
        "Role": "Capture Manager",
        "Name": "Doe, Jane",
        "Email": "[email protected]"
      }
    ]
  }
}]

I can get the data I want using the following:

string json = [see above];
JArray? obj = JsonConvert.DeserializeObject<JArray>(json);

foreach (var single in obj)
{
    JToken? pocs = single.Value<JToken>("Staff")?.Value<JToken>("Pocs");
    if (pocs != null)
    {
       foreach(var poc in pocs)
       {
           if (poc.Value<string>("Role") == "Capture Manager")
           {
               [Do something with the resulting JToken/values]
           }
       }
    }
}

I feel like there has to be some Linq to help me here, but everything I’ve tried thus far has been unsuccessful. I have no control over the JSON input, I just need to deal with it as is. Any help would be greatly appreciated!

3

Answers


  1. As mentioned by @gunr2171 in the comment, you can work with JSON Path.

    using Newtonsoft.Json.Linq;
    using System.Collections.Generic;
    
    JArray jArray = JArray.Parse(json);
    IEnumerable<JToken> jTokens = jArray.SelectTokens("$..Staff.Pocs[?(@.Role == 'Capture Manager')]");
    

    The above JSON Path:

    $ – From root element.

    ..Staff – Deep scan the element node with the Staff field.

    .Pocs – Access to the dot-notated Pocs child.

    [?(<expression>)] – Filter expression.

    @.Role == 'Capture Manager' – Current node with the filter expression that matches the Role field value equal to "Capture Manager".

    Reference:

    1. Querying JSON with complex JSON Path
    2. Cheatsheet for JSON Path

    Working with System.Linq and Newtonsoft.Json.Linq.

    using Newtonsoft.Json.Linq;
    using System.Collections.Generic;
    using System.Linq;
    
    IEnumerable<JToken> jTokens = jArray.Children()
                    .Values("Staff")
                    .SelectMany(x => x["Pocs"])
                    .Where(x => x.SelectToken("Role").Value<string>() == "Capture Manager")
                    .ToList()
    
    Login or Signup to reply.
  2. you can try this

    var jArr = JArray.Parse(json);
    
    JObject person = (JObject) jArr.Children()
              .Values("Staff")["Pocs"].First()
              .Where(s =>  (string)s["Role"]== "Capture Manager")
              .FirstOrDefault();
    
    string name = (string)person["Name"]; // "Doe, Jane"  
    
    Login or Signup to reply.
  3. You could always create a POCO and deserialize the JToken to that class object. This would allow you to create an array of that POCO from the JToken and use regular linq statements to work with it. It would take a bit more effort, but it might be easier to work with.

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search