How to parse null parameters in scripts

I'm trying to write a script transform that will take the JSON below and delete any leaf nodes with {label: null}.

[
  {
    "icon": {
      "path": "XXX",
      "style": {},
      "color": ""
    },
    "expanded": true,
    "label": "XXX",
    "data": {
      "EqType": XXX,
      "EqEnabled": true,
      "EqName": "XXX",
      "EqPath": "XXX",
      "EqID": 1
    },
    "items": [
      {
        "icon": {
          "path": "XXX/XXX",
          "style": {},
          "color": ""
        },
        "expanded": true,
        "label": "XXX",
        "data": {
          "EqType": "XXX",
          "EqEnabled": true,
          "EqName": "XXX",
          "EqPath": "XXX/XXX",
          "EqID": 1
        },
        "items": [
          {
            "icon": {
              "path": "XXX/XXX",
              "style": {},
              "color": "var(--error)"
            },
            "expanded": false,
            "label": "QA Test Area",
            "data": {
              "EqType": "XXX",
              "EqEnabled": false,
              "EqName": "QA Test Area",
              "EqPath": "XXX/XXX/XXX",
              "EqID": 11
            },
            "items": [
              {
                "icon": {
                  "path": "material/block",
                  "style": {},
                  "color": "var(--error)"
                },
                "expanded": false,
                "label": null,
                "data": {
                  "EqType": "XXX",
                  "EqEnabled": null,
                  "EqName": null,
                  "EqPath": null,
                  "EqID": null
                },
                "items": []
              }
            ]
          }
        ]
      }
    ]
  }
]

However, I have not been able to figure out a way to write a Python conditional that checks if label: null. This is what I attempted to do in the if statement below:

def transform(self, value, quality, timestamp):
	for enterprise in value:
		for site in enterprise["items"]:
			for area in site["items"]:
				for line in area["items"]:
					if line["label"] == None:
						del line

If someone could tell me how to check if a perspective property is null in Python, that would be very helpful.

try

if line["label"] is None:

or

if not line["label"]:

1 Like

You should review the error(s) you are seeing closely, I am guessing you are seeing things like:

name 'null' is not defined, etc.

You are not dealing with a None, you are dealing with a variable written as "null", if you want it to be treated as None then set that variable at the start of the script:

null = None

This is also true for variables like "true" because Python's true bool is: True

I'm not getting any errors when I run the script. My problem is that I would like for the script to delete any nodes with label: null from the json tree, but the script is not doing so, instead returning the initial json tree with zero modifications whatsoever.

Did you remove the return at the end of the transform?

It is assumed the custom script will end with a Return comment that is a single output value, dataset, document, or other type of data.

I've ensured that there is a return statement at the end of the transform. I also cloned value into the return variable tree to ensure that the code is not returning the original value variable.

def transform(self, value, quality, timestamp):
	tree = value[:]
	
	for enterprise in tree:
		for site in enterprise["items"]:
			for area in site["items"]:
				for line in area["items"]:
					if line["label"] == None:
						del line
	
	return tree

Unfortunately. I'm still getting the same behavior. tree still contains the item

{
  "icon": {
    "path": "material/block",
    "style": {},
    "color": "var(--error)"
  },
  "expanded": false,
  "label": null,
  "data": {
    "EqType": "line",
    "EqEnabled": null,
    "EqName": null,
    "EqPath": null,
    "EqID": null
  },
  "items": []
}

with label: null that I am trying to remove from the json tree.

What @danieltalbot said.
JSON defines "a key with no value" with the keyword null (not in quotes).
Python defines the same concept as a singleton object automatically imported as None.
The idiomatic way to check whether something is null/not present in Python is with if x is None: or if x is not None:.

Like this?

def transform(self, value, quality, timestamp):
	tree = value[:]
	
	for enterprise in tree:
		for site in enterprise["items"]:
			for area in site["items"]:
				for line in area["items"]:
					label = line["label"]
					if label is None:
						del line
	
	return tree

I attempted this, but the code is acting the same as before. It is not deleting the line node with label: null from tree.

I'm guessing that area["items"] is an array of line objects.

Try this:

def transform(self, value, quality, timestamp):
	tree = value[:]
	
	for enterprise in tree:
		for site in enterprise["items"]:
			for area in site["items"]:
				for i, line in enumerate(area["items"]):
					label = line["label"]
					if label is None:
						del area[i]
	
	return tree

I attempted your code, and it seems to be attempting to perform the deletion, thankfully.

However, the script errors out because the del statement throws the error TypeError: 'com.inductiveautomation.perspective.gateway.script.DotReferenceJythonMap' object doesn't support item deletion

You need to make a copy of the tree, probably recursively, where you simply do not copy the items you would otherwise delete.

1 Like

So far, the only working solution I've managed is by converting value from a JsonifiableArrayList to a List[Dict], then using a line_index variable and the pop() method to remove the line from the area.

def transform(self, value, quality, timestamp):
	import json
	tree = value[:][0].toString()
	tree = [json.loads(tree)]
	
	for enterprise in tree:
		for site in enterprise["items"]:
			for area in site["items"]:
				line_index = 0
				for line in area["items"]:
					label = line["label"]
					if label is None:
						area["items"].pop(line_index)
					line_index += 1
	
	return tree

For some reason, the del keyword does not remove the line from the items list, but pop() does.

def transform(self, value, quality, timestamp):
	for enterprise in value:
		for site in enterprise["items"]:
			for area in site["items"]:
				area['items'] = filter(lambda l: l['label'] is not None, area['items'])
				# area['items'] = [l for l in area['items'] if l['label'] is not None]
	return value

Didn't try it but I believe it should do what you want.
In comment, a comprehension version if that looks clearer to you.

2 Likes