Search code examples
c#arraysjsonlinqlambda

Get a value from a list where a condition is true using Select()


I have a JSON array where I need to get the CustomerId value from property where the CustomerId property exits for that section, I'm trying below to loop through the categories and skip the one that has no CustomerId property down its properties tree

var customerId = "";
    foreach (var category in JObject.Parse(someData)?["categories"])
            {
                val = category?["sections"].FirstOrDefault()
                ?["areas"]?.FirstOrDefault()
                ?["components"]?.
                ?["variables"]?.FirstOrDefault()
                ?["properties"]
                ?["customerId"]?.ToString();

                if (val == null)
                    continue;
                else
                {
                    customerId = val;
                    break;
                }

            }

Problem is this looks inefficient (less readable) in the sense that I imagine there is a nice .Select that can be used to get the same result without going forEach element and check if the property is null.

Please not this is not an issue I have, this is working, I'd only like to do this in a more readable way using Select instead of ForEach.

Sample JSON Data

 {
      "categories": [
        {
          "identifier": "cat1",
          "sections": [
            {
              "identifier": "030615e9-67f9-43ca-a06e-194e7afadccb",
              "properties": {},
              "areas": [
                {
                  "identifier": "1206f27b-d354-4bfa-9b5e-1fe6b4f7cc83",
                  "componenets": [
                    {
                      "identifier": "49e4550f-8001-4d32-b796-a7ad4423e118",
                      "type": "Product",
                      "variables": [
                        {
                          "identifier": "0d260417-fa6d-492b-85f1-dc2565bc4805",
                          "properties": {
                        
                            "description": ""
                            
                          }
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        },
        {
          "identifier": "cat2",
          "sections": [
            {
              "identifier": "00b5bfa6-f482-47c2-bdd7-aaac273b7772",
              "properties": {},
              "areas": [
                {
                  "identifier": "1ca9d051-5ec0-45af-84bc-561cd7620efa",
                  "componenets": [
                    {
                      "identifier": "c73b1e52-3acd-4020-8fc5-adfef7d633b4",
                      "type": "Customer",
                      "variables": [
                        {
                          "identifier": "0064e872-5c7f-4ec7-a2d6-e8b35b94bd1d",
                          "properties": {
                            "customerId": { "Text":"MainId",
"Value":"A12123"
}
                          }
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }

Solution

  • An alternative approach is to use .SelectTokens from the JSON.NET library (which it appears you are using). Assuming parsedJson is your parsed root object as a JObject:

    var customerId = parsedJson.SelectTokens("$.categories..sections..areas..componenets[?(@.type == 'Customer')]..variables..properties.customerId.Value")
        .FirstOrDefault()?
        .ToString();
    

    This essentially finds the first entry where a component has a type "Customer", and a property "customerId" and returns that customerId as a string.

    Strictly speaking you don't need the query on type ([?(@.type == 'Customer')]) but I got the impression that's what you were going for. Without it the query would look like this:

    "$.categories..sections..areas..componenets..variables..properties.customerId.Value"
    

    See more about SelectTokens in the docs here.