Using HTTP Get and trying to parse the data as JSON

I am trying to pull some data from a Vorne XL system. I have the HTTP Get setup as a gateway script using system.net.httpClient and working. I format the return as JSON but there is a nested list with the info I actually want. I am still learning scripting, so what is a good way to pull out the variable in the list. Currently it is the [[u'running']] status but changes a lot so I can't just look for that text.

{'data': {'events': [[u'running']]}, 'meta': {'device_id': u'MAC', 'data_version': u'1.6', 'asset_id': u'id'}}

Assuming you have it a variable called payload and you turned it into a dictionary with system.util.jsonDecode

events = payload['data']['events'] # this will give you the list of lists
firstEvent = events[0]

Seems like you could get multiple events since they're sending you a list of lists so you could do different logic on the events variable to see if any of those inner lists have running or what have you.

In python use the keys to access the value. For instance getting the device Id is simple as

deviceId = payload['meta']['device_id']

Thank you. I was able to pull out the first item in the list, convert to string, then just slice out the 'u' character that it adds to all the entries.

Any time you find yourself doing this ridiculous operation, it means somewhere you've turned an object into a string with jython repr(), which show unicode strings with that prefix.

Don't stringify objects!

system.net.httpClient will interpret JSON payloads for you and deliver the payload as a proper jython hierarchy of dictionaries, lists, and primitive elements. Just access them that way instead of playing fragile games with strings.

That is how it's showing up after just using the .json attribute of the httpClient on the response. Not sure if I'm doing it wrong or if it's the response structure from the Vorne system.

I have seen cases where an API wraps a value in an extra sets of quotes, so the response.json call results in the inner string and you have to do another call to system.util.jsonDecode() to get it into a dictionary/list.

"Showing up" means some form of printing, which is stringification, and collapses the object structure.

Log type(response.json) to see what I mean. The .json attribute is documented to return an object, not a string, as long as the API provided the proper Content-Type header.

Doing a print type(response.json) and then checking wrapper.log shows it as dictionary

Then access its elements with dictionary syntax, and inner elements per their types.

If you share the formatted JSON that actually gets delivered, we can help.

Or log system.util.jsonEncode(response.json, 2). (You'll have to extract from the wrapper.log to get the properly indented result.)

Here is everything,

#set the client
	client = system.net.httpClient()
	# Set up the request for Monoblock status info
	MBstatusRequest = client.get("http://x.x.x.x/api/v0/channels/process_state/events/current?fields=process_state")
	#print the status request, a code of 200 at the end means Vorne saw a valid request
	print MBstatusRequest
	MBstatusData = MBstatusRequest.json
	print MBstatusData
	print system.util.jsonEncode(MBstatusRequest.json, 2)

The log for printing just the request

<Response@5393670 'http://x.x.x.x/api/v0/channels/process_state/events/current?fields=process_state' [200]>

The log for printing with request.json

{'data': {'events': [[u'running']]}, 'meta': {'device_id': u'nnn', 'data_version': u'1.6', 'asset_id': u'nnn'}}

The log for printing using system.util.jsonEncode()

 {
   "data": {"events": [["running"]]},
   "meta": {
     "asset_id": "nnn",
     "data_version": "1.6",
     "device_id": "nnn"
   }
 }

So, the target you want should be response.json['data']['events'][0][0]. The list of lists at events is a bit odd. You may actually have to iterate through inner or outer lists depending on the variety in the responses.

Also, calling system.net.httpClient() on every API call is very expensive, and will leak memory. You should establish the client instance in a top level variable of a project library script (outside any def) and re-use it in the function called from your event.

Or, if you're on 8.3+ (and don't need to customize the http client instance):

MBstatusRequest = system.net.httpClient.get("http://x.x.x.x/api/v0/channels/process_state/events/current?fields=process_state")
#print the status request, a code of 200 at the end means Vorne saw a valid request
print MBstatusRequest
MBstatusData = MBstatusRequest.json
print MBstatusData
print system.util.jsonEncode(MBstatusRequest.json, 2)

Yes, I am on 8.3. So I don't need to do it in the project library? I initially thought to put it in the gateway events as a scheduled script every minute or two, and to write the result to a memory tag. That way every open client isn't running the script.

As of 8.3.0, system.net.httpClient is both an instance of JythonHttpClient and the factory method to create new client instances (precisely to avoid the memory/resource leak Phil talked about).

So moving to the project library in this case is less mandatory than Phil suggests, though may still be worth it for the sake of encapsulation.