0

I am using an api for a shopping cart that has some complex json (very complicated to me) data structured like in my screenshot below. In this scenario in my code I am trying to fix an error which I am going to explain by illustrating the data and how its structured as I am very new to JSON and arrays.

enter image description here

This is from the Visual Studio json reader of the data that belongs to an order placed by a customer. This item at the index of [0] has a customFields which has a value.

When a customer completes a purchase, some items they bought can have custom fields, like the size of a shirt (Large) or (Medium) or (Small) etc... In the JSON these customFields have a value which in this case is the size of the shirt for me to display at the thank you page so the customer knows what size he bought. Essentially I am trying to have the data ready to pass to the thank you page view.

When I am calling for these items in my controller, the code only works if ALL the items that were purchased have a customFields. If the customer buys something like a coffee mug that has NO custom fields, then the application breaks because I guess my code is only accounting for items that actually have customFields.

This is the code that I have so far that only works when ALL items that were purchased have a custom field. This is inside my controller.

public ActionResult Thankyou(string token)
    { 
       int itemsCountAddedToCart = (int)obj["items"].Count();

            var items = obj["items"].Select(o =>
            new Item
            {
                name = o["name"].ToString(),
                quantity = int.Parse(o["quantity"].ToString()),
                price = double.Parse(o["price"].ToString()),
                image = o["image"].ToString(),
                url = o["url"].ToString(),

                //This customFields is what works, but only if all items had custom fields.
                customFields = o["customFields"][0]["value"].ToString(),
                                        
            });

            thankYouViewModel.OrderItems = items;
     }



//ThankYou View Model that loads hold the data to be able to show in the view.
public class ThankYouViewModel
{
    public IEnumerable<Item> OrderItems { get; set; }
}

public class Item
{        
    public string name { get; set; }
    public double price { get; set; }
    public int quantity { get; set; }
    public string image { get; set; }
    public string url { get; set; }

    //customFields
    public string customFields { get; set; }
}

So that code above works, but breaks when I have items that do not have customFields. This is the error that I get:

System.ArgumentOutOfRangeException: 'Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index'

enter image description here

So how should my code look where its currently breaking so that it can account for situations where one of the items from the JSON does not have a customFields attribute? I am very stuck and have tried to add some conditional statements but did not work because I am dealing with some complex json I do not understand very well yet.

CodeQuest
  • 121
  • 1
  • 14
  • If the Client choosed more than one custom Field? (Lets say, a Mediun sized T-Shirt, that also wants to be Purple) you are referencing only the first elemente of customFields – J.Salas Feb 23 '22 at 08:56
  • I am handling the colors with different logic that is not part of customFields. So in my case color selection is being handled in the product title, the customer just goes to the URL of the color he wants. – CodeQuest Feb 23 '22 at 09:02
  • I think a [simple check](https://stackoverflow.com/questions/37364774/how-to-check-if-json-array-is-empty-in-c) here can help. – Xiang Wei Huang Feb 23 '22 at 09:06
  • @XiangWeiHuang where would I add that check in my code? I tried adding it inside the new Item { } but the syntax gets errors since its not allowed in there. – CodeQuest Feb 23 '22 at 09:08
  • It was only an example. In the pic of the JSON you provided, `customFields` is an **array** of objects, you are coding like it will be holding _always_ a single element – J.Salas Feb 23 '22 at 09:08
  • The issue in OP's code is that some of the properties might just not exist. For example, `customFields` like you noticed, or what if `customFields` exist but it's an empty array? `o["customFields"][0]` assumes it has at least 1 item(`[0]th item`) - that assumption will break if it's empty. What if `"value"` doesn't exist when `o["customFields"][0]` is there? Of course, if the api ensures that some of them absolutely exists then it's safe to skip some conditions; but it's not lack of understanding towards json like OP worried, instead, the need to be careful around enumerables generally. – Xiang Wei Huang Feb 23 '22 at 09:33
  • The ternary idea J.Salas provided is a good idea to workaround blocks where you can't place `if`, which might be the issue OP's facing here. By the way, the name `customFields` in `Item` might be confusing because it's actually a single string. This reminds me, if you're willing to create a class `Item` in your code, instead of string parsing, you might want to check out `Newtonsoft.Json` or `Json.NET`, as these supports parsing into custom classes while handling nulls by themselves. – Xiang Wei Huang Feb 23 '22 at 09:40
  • You're reinventing a wheel here; paste your most complex json into http://app.QuickType.io – Caius Jard Feb 23 '22 at 14:30

2 Answers2

2

If you want to forget the possibility of more than one element in the customFields array, and only cast the first element value to a string, then use this:

 customFields = (o["customFields"] == null || o["customFields"].Count() == 0)?null:o["customFields"][0]["value"].ToString(),
J.Salas
  • 1,268
  • 1
  • 8
  • 15
  • `o["customFields"]?.FirstOrDefault(null)?["value"]?.ToString()` maybe? Not sure if `o["customFields"]` will throw exception or return null on no result though – Xiang Wei Huang Feb 23 '22 at 09:22
  • 1
    Yes, but I didn't know the c# version OP is using so I went for a generic solution – J.Salas Feb 23 '22 at 09:42
  • 1
    Thank you @J.Salas , this implementation seems to do the trick for the moment. No loner getting that error. I will however work on changing my code a bit so that I can grab more than 1 element in that array since that is the correct way it should be done with more flexibility (I'm still learning hahaha). – CodeQuest Feb 23 '22 at 21:27
0

With customFields = o["customFields"][0]["value"].ToString(), you directly receive the value from the customFields Array. If there is no Array in your case then there is nothing to get.

I would recommend you to check if your customFields exists:

var item = new Item ();
item.name  = o["name"].ToString();
item.quantity  = int.Parse(o["quantity"].ToString());
item.price = double.Parse(o["price"].ToString());
item.image = o["image"].ToString();
item.url = o["image"].ToString();

if(o["customFields"] != null)
{             
   item.customFields = o["customFields"][0]["value"].ToString();
}
PluggeRo
  • 74
  • 4
  • I tried to add an if statement in there, but its not syntax that visual studio allows. The if statement just turns red with errors since its invalid expression. – CodeQuest Feb 23 '22 at 09:06
  • then you need to initialize the class in another way. I will edit my answer. – PluggeRo Feb 23 '22 at 09:08
  • OP is using o instead of json and I am not quite familiar with has here. Is the code pseudo? – Xiang Wei Huang Feb 23 '22 at 09:35
  • @XiangWeiHuang the code is a lambda function, o is defined on the line where they typed "var items = obj["items"].Select(o =>". It will be set to each item in the array obj["items"] and the code in OP's function run once for each of those items – Joon Feb 23 '22 at 09:41