cancel
Showing results for 
Search instead for 
Did you mean: 

WEB API - JSONPath - Returned Multiple Tokens

AndrewWilson
Level 3
Hi All,

I'm wondering if I can get some advice, to maybe point me in the right direction. I'm currently trying to extract a collection, from within a collection, returned from the SharePoint GRAPH API.

To take an example straight from Microsoft, I'm trying to extract "fields" as a complete collection for every item. Please note that in my example I expect different headers under fields, so defining the headers will not work.
{
  "value": [
    {
      "id": "2",
      "fields": {
        "Name": "Gadget",
        "Color": "Red",
        "Quantity": 503
       }
    },
    {
      "id": "4",
      "fields": {
        "Name": "Widget",
        "Color": "Blue",
        "Quantity": 2357
       }
    },
    {
      "id": "7",
      "fields": {
        "Name": "Gizmo",
        "Color": "Green",
        "Quantity": 92
       }
    }
  ]
}​

What my current issue is, I'm pulling around 3900 SharePoint items from a SharePoint List, then looping through the collection, and merging each line to a new collection becomes a very long task.

I've tried a couple different methods, following the Blue Prism 6.4 - Web API User Guide, however I'm unable to get past the "Internal : Unexpected error Path returned multiple tokens."

At the moment my main method has been to set the type to collection, JSONPath, and setting the path to $..fields
or $.value['fields']

I believe this may be because each "Value" has its own "Fields" collection, which means I need some form of a ForEach loop using custom code to extract this information. 

If possible, could somebody point me in the right direction to lookup what's needed to do this, or even better have some previous examples of others with a similar issue?

I've found these examples on community so far:

View Question

Blue Prism Community

However I've been struggling to adapt the code to meet the needs of this work.

Any help appreciated

1 BEST ANSWER

Helpful Answers

AndrewWilson
Level 3

@ewilson Thank you very much for explaining it. That helped massively.

Given I could not use the JSON Pathing I've gone down the custom code route, and put together some work that has thankfully worked. I'm now pulling together around 4000 items in a completed detailed list in less than 30 seconds!

For anybody with a similar issue, wanting to pull SPO List item details from a large SharePoint list, here are a few pointers.

You will need to create an additional API to pull the column headers​. I recommend reading here List columnDefinitions in a list - Microsoft Graph v1.0 | Microsoft Docs and adding this to Blue Prism existing SPO GRAPH API on DX.
Name:
36894.pngParameters:
36895.pngRequest:
36896.pngHeaders:
36897.pngResponse:
If you have over 200 columns in SharePoint I recommend not using Json Path $.value and instead just $, and then getting the remaining headers with GRAPHs "NextLink" which is returned on the top level. 
36898.png
Now that the API was completed and working I could now get the headers. I stored these in a collection, then wrote these out to a CSV (for use later in some code) then added additional fields that are not a header to the csv. (For example the eTag for referencing the items)
36899.png
This resulted in a long csv with all my headers. 
36900.png
Now using the "Get Items in List by Fields" Web API Action, return the "Response Content" to a text data item. (This is the JSON we will be using)

I then created a custom code object, added "Response_Content" and "columnHeaders" as the inputs, and "sharePointListItems" as the output. 
36901.png
36902.png
Then the secret sauce, the code. (I'll include a copy at the bottom)
36903.png
What we are doing is loading the response back into a JSON, and then moving up a level to "Value".
36904.png
We then split the CSV headers we got from SharePoint into an array to loop through. 
36905.png

Then we create all our headers for the data in SharePoint by looping through all items in the CSV creating a new column for each one.
36906.png
Now we loop through all the objects from the JSON, taking the fields data (This is where all the SharePoint list data is stored)
36907.png
Now again, for each field in the csv from earlier, we call the fields value for that column. and store it in the row, before adding the completed row to the table
36908.png
Then finally we output the table 
36909.png
Running this code resolved my issue, as I can now pull thousands of records in around 30 seconds. I hope what I found may help some other people out there with a similar issue.

@ewilson Thanks again for your help on understanding Json structure! 

Here is the code for compiling the headers and response together:

// Load Response
JObject responseObject = JObject.Parse(Response_Content);
JArray topLevel = (JArray)responseObject["value"];

//Get Headers CSV and create table
string[] headers = columnHeaders.Split(',');

//Create Headers for dataTable
var dataTable = new DataTable();

foreach (string header in headers)
	{
	dataTable.Columns.Add(header);
	}

//Loops each JSON Object and gets the "Fields" data
foreach (JObject Item in topLevel)
	{
	DataRow row = dataTable.NewRow();  
	JObject fields = (JObject) Item["fields"];


	// Cycles each column header in headers, and polls fields for that information
	foreach (var header in headers)
		{
			row[header] = (string) fields[header]; 
		}

	dataTable.Rows.Add(row);
	}

sharePointListItems = dataTable;

View answer in original post

10 REPLIES 10

ewilson
Staff
Staff
@AndrewWilson,

Give this a try:

$.value
  • .fields

    Cheers,
    Eric​
  • AndrewWilson
    Level 3

    @ewilson

    Thanks for your response!

    36884.png

    I've just tried this. Sadly still getting ​

    "Internal : Unexpected error Path returned multiple tokens."

    ewilson
    Staff
    Staff
    @AndrewWilson,

    Ok, let’s try a basic solution. Try setting your JSON Path to just $.value​

    This should return a Collection with an internal Collection column called fields. It will include columns for the other bales too, but let’s see if we can at least get this part.

    Cheers,
    Eric

    AndrewWilson
    Level 3

    @ewilson

    No problem, I've already tested this far, and yes thankfully this works.​
    36885.png
    36886.png
    We're getting all of the fields collections through, no problem. and these fields have the data within them.

    36887.png

    ewilson
    Staff
    Staff

    @AndrewWilson,

    That’s good. You at least have a path forward.

    The previous expression is the standard way to iterate a JSON array to pull out child fields. Are there cases where the fields object has more or different children within it than just the  Name, Color, and Quantity values shown in your example?

    Cheers,
    Eric​​

    AndrewWilson
    Level 3
    @ewilson

    Yes, so the way GRAPH is returning data in the fields​ collection, is it will only return custom fields if they are filled in. So we will receive collections with different numbers of headers. However they are the same same set for 50 or so total headers overall.

    Let me modify the previous example to demonstrate better, working off Microsofts example
    {
      "value": [
        {
          "id": "2",
          "fields": {
            "Name": "Gadget",
            "Type": "App"
            "Color": "Red",
            "Quantity": 503
           }
        },
        {
          "id": "4",
          "fields": {
            "Name": "Widget",
            "Color": "Blue",
           }
        },
        {
          "id": "7",
          "fields": {
            "Name": "Gizmo",
            "Type: "Item"
            "Quantity": 92
           }
        }
      ]
    }​​


    ​There are around 50 headers in total, with each returning around 46 headers, give or take a few, due to missing information.

    Edit: Just to confirm, "value", "id", and "fields" will always be present

    ewilson
    Staff
    Staff
    @AndrewWilson,

    Thats why the original JSON Path statement won’t work in this case. Blue Prism is defining a DataTable object based on the elements found in the very first fields object.​ Later in the processing, if a fields object is encountered with different elements it causes an exception because it can’t be mapped to the previously defined DataTable.

    The more generic JSON Path statement works because the DataTae for the fields object is defined every time a value entry is processed.

    Cheers,
    Eric

    AndrewWilson
    Level 3
    @ewilson

    could you explain it a little more for me, just so it sinks in. Why does the Blue Prism example in "Blue Prism 6.4 - Web API User Guide" work, when calling $.staff-database['employee'] for returning all employee values, if the column headers need to be the same? (id 1245 does not have a job-title attribute)

    36888.pngAs explained here:
    36889.png
    I understood the issue I was having is that when calling "value" there are multiple "fields" for each id returned, which is what was giving us the "Multiple tokens" error

    ewilson
    Staff
    Staff
    @AndrewWilson,

    How familiar are you with JSON? The first thing to understand is the example from the Web API guide is actually invalid JSON. :face_with_tears_of_joy: There should be commas at the end of each line highlighted in read and no comma at the end of the line highlighted in blue. The blue one isn't a deal breaker, but based on the spec it's invalid.
    36890.png
    The next thing to understand, and a key difference between the two examples, is that in the Web API example the employee element is a JSON array. So a JSON array within a JSON object (i.e. staff-database) whereas in the SharePoint example the fields element is a JSON object within a JSON array (i.e. value). This makes a difference in how you access the child elements.

    Consider the following JSON Path:
    $.staff-database["employee"]​


    NOTE:
    The JSON Path expression above returns the same output at $.staff-database

  • because there's only a single child element within the staff-database item and that child element is a JSON array.

    That JSON Path returns the following JSON:
  • 36891.png
    This is a JSON array containing a JSON array of JSON objects. You can identify a JSON array by looking for [ ] (square brackets). A JSON object is indicated by { } (curly brackets).

    If you use the following JSON Path on your original SharePoint example:
    $.value

  • You'll get the following output:
    36892.png
    This is a JSON array of JSON objects. So we have a slightly different JSON definition: JSON Array, JSON Array, JSON Objects vs JSON Array, JSON Objects

    If you wanted to get a similar a result from the Web API example JSON, you would need to use a JSON Path expression like this:

    $.staff-database["employee"]

  • This would give you the following output:
    36893.png
    If Blue Prism is parsing a JSON array of objects it looks at the very first object in the array (assumption that arrays always contain items that are same type/definition) and uses that as the blue print of the DataTable it creates in memory to contain a row for each item in the array. It performs this interpretation exactly 1 time. If Blue Prism is parsing an array of objects within another array, it's interpreting the JSON object definition every time it sees a new row in the contained array. In that case, the blue print of the DataTable can actually change from one row to the next.

    Does that help?

    Cheers,
    Eric