Pulling data from web via REST BPM

I’m setting up a BPM to get data from a remote server via a REST get call. How do you declare the object for the data in the response to fill in? I’ve done rest BPMs where Epicor data is sent TO another databases but not the reverse.

With the help of Rest bpm thread I’m pretty close. I’ll display the returned data but it can’t be parsed as I used dynamic as the type. I’ve tried several ways to create a JObject but none work.

image

Code snip of attempting to declare the object:

...
  using (WebClient wclient = new WebClient())
  {     
    try
    {   
      wclient.Headers.Add("Ocp-Apim-Subscription-Key", key);
      string uri = host + path + query;
      dynamic response = wclient.DownloadString(uri);

  JObject jsonObj = JObject.Parse(parsedJson);
  (
      tdd = (string)jsonObj["value"][0]["Target_Delivery_Date"];
      SLID = (string)jsonObj["value"][0]["SLID"];
      OppID = (string)jsonObj["value"][0]["OppId"];
  )
  
  parsedJson = JsonConvert.DeserializeObject(response); 

///    dynamic parsedJson = JsonConvert.DeserializeObject(response);  //dynamic will display data but not able to parse after Deserializing    
Debug = "parsedJson: "+ parsedJson;
  }
   

That gives me error:

System.Drawing.Bitmap CS0103 The name ‘parsedJson’ does not exist in the current context

One of the easiest ways to accomplish this is to use a dynamic variable to store the response.

Then you can access the contents by name/position/whatever you’re expecting
In the example below, I am deserializing a response (in this case I know the response type because I also used a model, but in your case you don’t have to)

var json = JsonConvert.DeserializeObject<CreatedTaskResponse>(response.Content);                               
var taskID = json.Id;

and then I am reading the response

Another example, wherein I am switching based on the response code returned:

switch (response.StatusCode)
{
     case System.Net.HttpStatusCode.BadRequest:
     {
         dynamic content = JsonConvert.DeserializeObject(response.Content);
         var value = content;
         return BadRequest(content);
     }
     case System.Net.HttpStatusCode.OK:
        default:
        {
          dynamic content = JsonConvert.DeserializeObject(response.Content);
           var value = content;
           return Ok(content);
        }
}

The other thing is that Microsoft is recommending not to use WebClient for new development, so you might consider using a different Http client in your code.

2 Likes

Thanks! I still got to be missing something I tried your code below. By chance do you have more of an example --especially one not using WebClient since it’s going away? I still get the full data set but the tgtDate still doesn’t want to display. I’ll try to beat my head on this tomorrow. LOL

Per the MS doc, Quickstart: Send a search request to the REST API using C# - Bing Entity Search - Azure Cognitive Services | Microsoft Learn I tried using HttpClient client = new HttpClient(); but Epicor did not like it. Though, we’re on 10.2.600.x for now.

image

Bad code below, why you no work?!

  using (WebClient wclient = new WebClient())
  {     
    try
    {   
      wclient.Headers.Add("Ocp-Apim-Subscription-Key", key); 
      string uri = host + path + query; 
Debug += Environment.NewLine+" 3b :"; 

      dynamic response = wclient.DownloadString(uri);   
Debug += Environment.NewLine+" 3c :"; 

      var parsedJson = JsonConvert.DeserializeObject(response.Content);
Debug = "3Data: "+ parsedJson;
      var tgtDate = parsedJson.Target_Delivery_Date;
Debug += tgtDate;  //Does not display anything 
    }
  }

What happens if you read the SLID variable instead?

I’m going to try a code up an example for you, brb

Here’s a crappy example of how your string response from the web service is being returned and parsed by the JsonConvert.DeserializeObject. In your case, you have 1 set of records, but if you had multiple, you could iterate over them like so, or access them by position.

using System;
using Newtonsoft.Json;

public class Program
{
	public static void Main()
	{
		string responseValues =@"[{'Target_Delivery_Date':'2021-01-18','SLID': '202011-WRKAf','OppId':'0065G00000WRKAfQAP'}]";

		dynamic foo = JsonConvert.DeserializeObject(responseValues);
		
		//shows the string as a json array
		Console.WriteLine(foo);		
		
		//iterate over the dynamic content
		foreach(var item in foo)
		{
			Console.WriteLine("Target Delivery Date " + item.Target_Delivery_Date);
			Console.WriteLine("SLID " + item.SLID);
			Console.WriteLine("OppID " + item.OppId);
		}
		
		//Or, access the object by name/index
		Console.WriteLine("Target Delivery Date " + foo[0].Target_Delivery_Date);
		
	}
}

Results:
image

1 Like

I had a similar issue when starting with REST from a BPM/client.
Easy to view not so easy to actually consume th returned data.
What worked best for me was using JToken to further split the parsed JSON.
How you define the breakdown is depending on the JSON response structure.
The below sample is in a get list uBAQ that is then run form a function so I can schedule it to refresh postcodes from our freight broker. This should make it Kinetic ready :wink:
Hope it helps

JObject o = JObject.Parse(consignmentResponse);
//Create JToken for Each Json Response Section
JToken errToken = o["errors"];
JToken objToken = o["object"];
       
// If Error section is blank, use object section to get Postcode details 
// Else show Error Message

int x = 0;
if (errToken.HasValues == false)  
{
  foreach(var xRow in o["object"])
  {
        
    string myDescription = (string)o["object"][x]["description"];
    var addressArray = myDescription.Split(',');
    string mySuburb = addressArray[0];
    string myPostcode = Regex.Replace(addressArray[1], @"\s", "");

2 Likes

@Aaron_Moreng thanks so much for the example, it was indeed setting up the foreach array. Never occurred to me to try it since there will only ever be a single return response.
I owe you a drink at the next years Insights.

1 Like

Yeah you’ll just want to be aware of what your responses could be. If it’s only ever going to be 1 row of data, you could do the access by index thing I showed too, just depends on your use case!

2 Likes

@josecgomez also had something in his REST nuget called a dynamic object I think… It is used for passing these datasets between rest calls if I am not mistaken. I know that isn’t exactly what you are getting at here, but in the future they may come in handy.

I may not have explained it 100% so feel free to correct me if I am wrong anyone. @jdewitt6029

1 Like

Hi @Aaron_Moreng

I am trying to do this but I get the following error:

Error: CS0518 - line 0 (0) - Predefined type ‘Microsoft.CSharp.RuntimeBinder.Binder’ is not defined or imported
Error: CS1969 - line 154 (713) - One or more types required to compile a dynamic expression cannot be found. Are you missing a reference?

This appears to suggest I am missing a reference, but I cannot work out what is missing?

What have I done wrong here?

Many thanks,

Steve

Hey Steve, I’m not exactly sure without seeing your code and what you’re trying to do, but that indicates you need to bring in a reference to Microsoft.CSharp.
I am a little confused why it’s doing that, but that’s where to start.

Are you writing this from a BPM or from a standalone project?

Hi, it’s a form customisation, here’s the code, I am hoping it is something daft I have missed?

It almost seems like you don’t have a high enough .NET version installed. The dynamic type was introduced in .NET 4.5/C# 4. Might want to look at what version you have
How to check .NET Framework version on Windows 10 | Windows Central

I have .Net 4,8 installed, not sure I could be running 10.2.700 without it?

That’s what I figured, I’m just wondering where that compile error is coming from…

Perhaps you need a “Using xxx;” specified but that’s just a guess.

I searched my PC for Microsoft.Csharp.dll and found it in on of the windows folders. I copied it to my EPICOR client folder and then was able to add it as a custom assembly and it now compiles successfully. So I need to understand if this is an EPICOR client issue of PC build issue?

Now I get an error when running the code. I put the result into a message:

image

Error from the code is

‘Newtonsoft.Json.Linq.JProperty’ does not contain a definition for ‘OKToProcess’

Hopefully this is easier to solve due to me being a noob to working with REST API and JSON data :blush:

I’ve never tried to do REST on a form. Could you trigger this REST call via a BPM by chance instead?

try just results.OKToProcess

1 Like

That worked, many thanks for the help.

So I just need to work out if the E10 client should include the Microsoft.Csharp dll or not?

Hi Randy,

I could do what I need using other methods, but there are a couple of reasons for going down this route, one is that I use the code is a few places and this is exactly what EPICOR functions are designed to do, to provide a central / common code base wherever possible. The only way to call a function in an EPICOR form customisation is using REST there are no business objects available to call here.

Also, we have our eye on the eventual move to Kinetic forms and are expecting that using REST within the Kinetic studio may be the way to go.

All in all it is a bit experimental, but we love a learning curve!