system.util.jsonDecode not decoding nested structures?

v8.1.0rc1

UPDATE: it seems I have a misunderstanding of json structures… working on it

I’m trying to make a function for development when we don’t have a PLC to convert all tags from opc to memory, as all the opc tag info is safely captured regardless of what data type it is.

I’m trying to make use of the clipboard and the Copy Json function in the tag browser context menu.

When I jsonDecode my clipboard tag json, it sort of works, but it only seems to be converting the root objects to python dicts, and leaves the nested ones as unicode strings…

This is my code so far (not finished):

from java.awt.datatransfer import StringSelection
from java.awt.datatransfer import Clipboard
from java.awt import Toolkit 
from java.awt.datatransfer import DataFlavor

def setup():
    global toolkit
    global clipboard
    toolkit =  Toolkit.getDefaultToolkit()
    clipboard = toolkit.getSystemClipboard()
    
def readText():
	setup()
	contents = clipboard.getContents(None)
	return contents.getTransferData(DataFlavor.stringFlavor)
json = readText()
json = system.util.jsonDecode(json)
print json
print 'converting...'

def convertToOPC(obj):
	print 'obj = ', obj, type(obj)
	if isinstance(obj,dict):
		print '   is dict'
		for key in obj:
			print 'key = ', key
			if key == "opcItemPath":
				obj["valueSource"] = 'opc'
			if isinstance(key, dict):
				print 'key is dict'
				obj[key] = convertToOPC(key)
	else:
		pass
		#if obj == "opcItemPath":
		#	obj["valueSource"] = 'opc'
	return obj

for key in json.keys():
	json[key] = convertToOPC(key)

print json

This is my test tag json:

{
  "readOnly": false,
  "dataType": "Int4",
  "writePermissions": {
    "type": "AllOf",
    "securityLevels": []
  },
  "name": "_TEST",
  "readPermissions": {
    "type": "AllOf",
    "securityLevels": []
  },
  "tagGroup": "Default",
  "parameters": {
    "Unit": {
      "dataType": "String",
      "value": ""
    },
    "TagPrefix": {
      "dataType": "String",
      "value": ""
    },
    "Format": {
      "dataType": "String",
      "value": "#,##0.0"
    },
    "PLCName": {
      "dataType": "Integer",
      "value": {
        "bindType": "parameter",
        "binding": "{PLCName}"
      }
    },
    "Description": {
      "dataType": "String",
      "value": ""
    },
    "Global.": {
      "dataType": "String",
      "value": ""
    }
  },
  "tagType": "UdtType",
  "enabled": true,
  "tags": [
    {
      "opcItemPath": {
        "bindType": "parameter",
        "binding": "ns\u003d1;s\u003d[{PLCName}]{Global.}{TagPrefix}.Avail"
      },
      "valueSource": "memory",
      "readOnly": false,
      "dataType": "Boolean",
      "writePermissions": {
        "type": "AllOf",
        "securityLevels": []
      },
      "name": "Available",
      "readPermissions": {
        "type": "AllOf",
        "securityLevels": []
      },
      "tagGroup": "Default",
      "enabled": true,
      "tagType": "AtomicTag",
      "opcServer": "Ignition OPC-UA Server"
    },
    {
      "opcItemPath": {
        "bindType": "parameter",
        "binding": "ns\u003d1;s\u003d[{PLCName}]{Global.}{TagPrefix}.ResetPB"
      },
      "valueSource": "memory",
      "readOnly": false,
      "dataType": "Boolean",
      "writePermissions": {
        "type": "AllOf",
        "securityLevels": []
      },
      "name": "Alarm Reset CMD",
      "readPermissions": {
        "type": "AllOf",
        "securityLevels": []
      },
      "tagGroup": "Default",
      "enabled": true,
      "tagType": "AtomicTag",
      "opcServer": "Ignition OPC-UA Server"
    }
  ]
}

And this is the resulting print statements from within the convert function only:

converting...
obj =  tagGroup <type 'unicode'>
obj =  dataType <type 'unicode'>
obj =  writePermissions <type 'unicode'>
obj =  readOnly <type 'unicode'>
obj =  enabled <type 'unicode'>
obj =  tags <type 'unicode'>
obj =  readPermissions <type 'unicode'>
obj =  name <type 'unicode'>
obj =  tagType <type 'unicode'>
obj =  parameters <type 'unicode'>

Okay, turns out I was just misinterpreting the decoded json and apparently forgot how dictionaries work…

But anyway, here are my functions to convert opc tags to memory and back. I should probably just look in the “tags” array for tags instead of looking in any array/dict. oh well…

from java.awt.datatransfer import StringSelection
from java.awt.datatransfer import Clipboard
from java.awt import Toolkit 
from java.awt.datatransfer import DataFlavor

### clipboard functions ###
def setup():
    global toolkit
    global clipboard
    toolkit =  Toolkit.getDefaultToolkit()
    clipboard = toolkit.getSystemClipboard()

def writeClipboard(text):
	setup()
	clipboard.setContents(StringSelection(text), None)
	
def readClipboard():
	setup()
	contents = clipboard.getContents(None)
	return contents.getTransferData(DataFlavor.stringFlavor)
### end clipboard functions ###

def convertTagSource(obj, valSource='opc'):
	if valSource not in ['opc','memory']:
		import sys
		sys.exit()
	# if the obj is a list, then run through it and convert any "memory opc" tags to opc
	if isinstance(obj,list):
		for i in range(len(obj)):
			obj[i] = convertTagSource(obj[i], valSource)
			
	# if the obj is a dict, check if it is a tag that is opc and make sure its valueSource defines it as opc
	if isinstance(obj,dict):
		if 'opcItemPath' in obj:
			obj["valueSource"] = valSource
			
		# 
		for key in obj.keys():
			if isinstance(obj[key], dict):
				obj[key] = convertTagSource(obj[key], valSource)
			if isinstance(obj[key], list):
				for i in range(len(obj[key])):
					obj[key][i] = convertTagSource(obj[key][i], valSource)
			
	# if its just a flat type, ignore it
	else:
		pass
	return obj

def convertTagJsonTo(convertTo='opc'):
	# make sure you copy some tag json first to the clipboard!
	tagsJson = readClipboard()
	tags = system.util.jsonDecode(tagsJson)

	if convertTo in ['opc','memory']:
		for item in tags:
			tags[item] = convertTagSource(tags[item], convertTo)
	
		tags = system.util.jsonEncode(tags)
		tags = tags.replace(":True", ":true").replace(":False", ":false") # json technically doesn't capitalise first letter of bool vals.. compare tools whinge
		writeClipboard(tags)
	else:
		system.gui.errorBox("You failed at setting the value source correctly... use 'opc' or 'memory' idiot", 'Idiot')

convertTagJsonTo('memory')
#convertTagJsonTo('opc')