简体   繁体   中英

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"
}
                          }
                        }
                      ]
                    }
                  ]
                }
              ]
            }
          ]
        }
      ]
    }

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 .

If you want to use the same logic as in you question, the code below is what you can use.

First you iterate over all the categories where you select the customerId field from. This will result in a list of customerIds where some are null and some might have a value.

The next SingleOrDefault takes the first item from that list which does have a value. This will be your string customerId .

Note: When there are no customerId's in your json, the customerId will be null . If you want to throw an exception instead of working with null, you can use First() instead of FirstOrDefault .

var customerId = JObject.Parse(someData)?["categories"]
                .Select(category => category?["sections"].FirstOrDefault()
                    ?["areas"]?.FirstOrDefault()?["components"]
                    ?["variables"]?.FirstOrDefault()
                    ?["properties"]
                    ?["customerId"]?.ToString())
                .SingleoOrDefault(customerId => customerId != null);

I have an optimized version:

var pJson = JObject.Parse(someData);
JToken? customerToken = pJson.SelectToken($"$..customerId");
var customerId = customerToken?.ToString();

I would first parse the JSON to list of objects and the use the Linq to fetch the list of CusomerIds where it is not null.

List<T> arrayItems = jsonBuidler?["categories"].ReadFromJson<List<T>>();
List<string> customerIds = arrayItems
   .Where(_ => CustomerId != null)
   .Select(_ => _.CustomerId);

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