# Perspective Scripting: Data Type Issue

**URL:** <https://forum.inductiveautomation.com/t/perspective-scripting-data-type-issue/108841>\
**Category:** Ignition\
**Tags:** ignition81, scripting, perspective\
**Created:** [July 23, 2025, 4:25pm UTC](https://forum.inductiveautomation.com/t/perspective-scripting-data-type-issue/108841 "2025-07-23T16:25:03Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![BQuacken](https://avatars.discourse-cdn.com/v4/letter/b/d2c977/32.png) [@BQuacken](https://forum.inductiveautomation.com/u/BQuacken)\
**Post date:** [July 23, 2025, 4:25pm UTC](https://forum.inductiveautomation.com/t/perspective-scripting-data-type-issue/108841/1 "2025-07-23T16:25:03Z")

</div>

In an effort to minimize bloat, I'm going to try to just describe the problem and the suspected culprits:

From a Message Handler on a Tree component, I am receiving a `TypeError` due to a function in my script returning `None`. I am feeding the Tree's `items` property to the function as `x`:

```python
x = self.props.items

def find_first(obj_to_inspect, val_to_find, listpath=()):
	    if isinstance(obj_to_inspect, dict):
	        # Omitted for brevity...
	    if isinstance(obj_to_inspect, list):
	        # Omitted for brevity...
	    if isinstance(obj_to_inspect, str):
	        # Omitted for brevity...

y = find_first(x, payload)

```

When I copy and paste the Tree's `items` into the script console as x and check the type with `type(x)`, I get the expected result that `x` is a `<type 'list'>`. My function `find_first()` therefore works as expected in the script console, and returns a proper data type - a tuple, for what it's worth.

However, when I check the type of `x` in the message handler by sending `type(x)` to a sibling Label, I get:

```python
class com.inductiveautomation.perspective.gateway.script.PropertyTreeScriptWrapper$ArrayWrapper

```

This explains why my function is returning `None`; it's looking for a `list`, `dict`, or `str`, but getting something different.

I've tried coercing `x` into a usable `list` via `x = list(self.props.items)`, but the type is just changed to `class org.python.core.PyList`, and the `find_first()` function still returns `None`.

So my problem really has two potential solutions:

1. How do I convert the Tree's `items` into a `list` so I can use it in the message handler's `find_first()` function?
2. If Option 1 can't be acheived, how do I alter the `if isinstance()` statements within the `find_first()` function so that they work with the Tree's `self.props.items` data type?

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [July 23, 2025, 4:28pm UTC](https://forum.inductiveautomation.com/t/perspective-scripting-data-type-issue/108841/2 "2025-07-23T16:28:11Z")

</div>

BQuacken, you gotta embrace [duck typing](https://en.wikipedia.org/wiki/Duck_typing) 😃

> [@Function to convert json string or python dict into ObjectWrapper](https://forum.inductiveautomation.com/t/function-to-convert-json-string-or-python-dict-into-objectwrapper/32861/16):
>
> Forcing each to the expected type while traversing makes a deep copy: def sanitize\_tree(element): if hasattr(element, '\_\_iter\_\_'): if hasattr(element, 'keys'): return dict((k, sanitize\_tree(element[k])) for k in element.keys()) else: return list(sanitize\_tree(x) for x in element) return element EDIT: Got rid of the unnecessary zip() and used only one keys() iterator instead of two.

---

<div class="post-metadata">

**Author:** ![BQuacken](https://avatars.discourse-cdn.com/v4/letter/b/d2c977/32.png) [@BQuacken](https://forum.inductiveautomation.com/u/BQuacken)\
**Post date:** [July 23, 2025, 5:57pm UTC](https://forum.inductiveautomation.com/t/perspective-scripting-data-type-issue/108841/3 "2025-07-23T17:57:45Z")

</div>

That makes a lot of sense! (And I'm embarrassed that I had no idea what that was, in spite of my name 😅)

Unfortunately - even though utilizing Justin's function you linked to is certainly better than just converting the top level list - the result is the same; I receive a `TypeError` due to `find_first()` returning `None`, and testing for `type(x)` - after running it through `sanitize_tree()` - still yields `class org.python.core.PyList` rather than `<type 'list'>` or something similar.

I feel like I'm missing something obvious...

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [July 23, 2025, 6:16pm UTC](https://forum.inductiveautomation.com/t/perspective-scripting-data-type-issue/108841/4 "2025-07-23T18:16:54Z")

</div>

Don't check the type. Neither with `type()` nor with `isinstance()`. Duck typing means to **use** the methods you expect for a list (iteration, whatever), and it'll walk like a duck (list) if it is duck-like (list-like).

---

<div class="post-metadata">

**Author:** ![BQuacken](https://avatars.discourse-cdn.com/v4/letter/b/d2c977/32.png) [@BQuacken](https://forum.inductiveautomation.com/u/BQuacken)\
**Post date:** [July 23, 2025, 6:34pm UTC](https://forum.inductiveautomation.com/t/perspective-scripting-data-type-issue/108841/5 "2025-07-23T18:34:24Z")

</div>

Hmm... Perhaps then it is time to expand on what I'm doing a little bit. The full `find_first()` function looks like this:

```python
def find_first(obj_to_inspect, val_to_find, listpath=()):
	if isinstance(obj_to_inspect, dict):
	    for key, value in obj_to_inspect.items():
	        item = find_first(value, val_to_find, listpath)
	        if item is not None:
	            return item
	if isinstance(obj_to_inspect, list):
	    for ind, value in enumerate(obj_to_inspect):
	        item = find_first(value, val_to_find, listpath + (ind,))
	        if item is not None:
	            return item
	if isinstance(obj_to_inspect, str):
	    if obj_to_inspect == val_to_find:
	        return listpath

```

I want to use this to find the page path - supplied to the message handler as the payload - within the Tree's `items`; it was added to the appropriate item's `data` previously. The list of indices to that item is what I really need, and I don't know how to arrive at that without using `isinstance()` as above.

---

<div class="post-metadata">

**Author:** ![lrose](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/lrose/32/62724_2.png) [@lrose](https://forum.inductiveautomation.com/u/lrose)\
**Post date:** [July 23, 2025, 6:36pm UTC](https://forum.inductiveautomation.com/t/perspective-scripting-data-type-issue/108841/6 "2025-07-23T18:36:59Z")

</div>

Obviusly, you should probably insure that you don't send a 'NoneType' object to the function, but if for some reason you can't do that, then just add a check:

```python
def find_first(obj_to_inspect, val_to_find, listpath=()):
    if obj_to_inspect and not isinstance(obj_to_inspect,str):
        if hasAttr(obj_to_inspect, ' __iter__'):
            if hasAttr(obj_to_inspect, 'keys'):
                #do dictionary work
            else:
                #do list work
    else:
        #do string work

```

---

<div class="post-metadata">

**Author:** ![BQuacken](https://avatars.discourse-cdn.com/v4/letter/b/d2c977/32.png) [@BQuacken](https://forum.inductiveautomation.com/u/BQuacken)\
**Post date:** [July 23, 2025, 6:38pm UTC](https://forum.inductiveautomation.com/t/perspective-scripting-data-type-issue/108841/7 "2025-07-23T18:38:37Z")

</div>

Oh!  
....  
🤦‍♂️ Yeah, that's probably it. Let me try that.

---

<div class="post-metadata">

**Author:** ![pturmel](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/pturmel/32/3295_2.png) [@pturmel](https://forum.inductiveautomation.com/u/pturmel)\
**Post date:** [July 23, 2025, 6:40pm UTC](https://forum.inductiveautomation.com/t/perspective-scripting-data-type-issue/108841/8 "2025-07-23T18:40:16Z")

</div>

I check (with `hasattr()`) for the `items` method for duck-typed dictionaries. (Or just try to iterate over `items()` and catch the exception.)

Then check for ` __iter__ ` or just attempt to iterate.

If both of those fail, I would then do the string search.

---

<div class="post-metadata">

**Author:** ![paul-griffith](https://sea1.discourse-cdn.com/inductiveautomation/user_avatar/forum.inductiveautomation.com/paul-griffith/32/37249_2.png) [@paul-griffith](https://forum.inductiveautomation.com/u/paul-griffith)\
**Post date:** [July 23, 2025, 6:56pm UTC](https://forum.inductiveautomation.com/t/perspective-scripting-data-type-issue/108841/9 "2025-07-23T18:56:24Z")

</div>

The key insight is that strict subtyping is just too narrow for Ignition's Jython environment.  
Jython models Java and Python as (basically) two completely distinct type hierarchies. Any Java code you interact with (literally everything written by IA) can return _either_ a "true" Python object or a Java object that Jython will automatically adapt. If it is close enough to a list, Jython will adapt it and it will **behave** as a list from your Jython code (e.g. Java arrays, `PyList`, `ArrayWrapper`, etc) -- but it will **not** be a subtype/instance of Python's `list` class.

You have no choice but to be more defensive in your code.
