[1276] How to access key values of property bound to object value?

I have an object property that’s essentially just a dictionary that I’ve bound another property to. How can I access the key values in an expression binding?

I’ve tried using jsonGet({value}, 'min') but it says error. Assuming because the 1st arg isn’t actually json…
Do I need to use a script transform? If so, is a basic script transform (e.g. only performing basic manipulation on the input value - no fancy calls to functions in the system library) any worse for performance for the client and the server than an expression?

Perhaps {value[“key”]} or {value}.key

No luck. I tried {value}['min'] as well

If you search the loggers for “JsonGet” you should find one. If you turn it to DEBUG and let the expression evaluate what ends up in the logs?

This is for my above jsonGet


com.inductiveautomation.ignition.common.gson.JsonSyntaxException: com.inductiveautomation.ignition.common.gson.stream.MalformedJsonException: Unterminated array at line 1 column 33 path $.min[3]

at com.inductiveautomation.ignition.common.gson.internal.Streams.parse(Streams.java:60)

at com.inductiveautomation.ignition.common.gson.JsonParser.parse(JsonParser.java:84)

at com.inductiveautomation.ignition.common.gson.JsonParser.parse(JsonParser.java:59)

at com.inductiveautomation.ignition.common.gson.JsonParser.parse(JsonParser.java:45)

at com.inductiveautomation.ignition.common.expressions.JsonGet.execute(JsonGet.java:36)

at com.inductiveautomation.ignition.common.expressions.FunctionExpression.execute(FunctionExpression.java:69)

at com.inductiveautomation.perspective.gateway.binding.PerspectiveExpression.execute(PerspectiveExpression.java:129)

at com.inductiveautomation.perspective.gateway.binding.transforms.expression.PerspectiveTransformExpression.execute(PerspectiveTransformExpression.java:72)

at com.inductiveautomation.perspective.gateway.binding.transforms.expression.ExpressionTransform.synchronousTransformInternal(ExpressionTransform.java:32)

at com.inductiveautomation.perspective.gateway.binding.transforms.AbstractSynchronousTransform.transform(AbstractSynchronousTransform.java:30)

at com.inductiveautomation.perspective.gateway.model.AbstractBindingHarness$TransformSequencer.transform(AbstractBindingHarness.java:259)

at com.inductiveautomation.perspective.gateway.model.AbstractBindingHarness$TransformSequencer.run(AbstractBindingHarness.java:272)

at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Unknown Source)

at java.base/java.util.concurrent.FutureTask.run(Unknown Source)

at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(Unknown Source)

at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(Unknown Source)

at java.base/java.lang.Thread.run(Unknown Source)

Caused by: com.inductiveautomation.ignition.common.gson.stream.MalformedJsonException: Unterminated array at line 1 column 33 path $.min[3]

at com.inductiveautomation.ignition.common.gson.stream.JsonReader.syntaxError(JsonReader.java:1568)

at com.inductiveautomation.ignition.common.gson.stream.JsonReader.doPeek(JsonReader.java:476)

at com.inductiveautomation.ignition.common.gson.stream.JsonReader.hasNext(JsonReader.java:414)

at com.inductiveautomation.ignition.common.gson.internal.bind.TypeAdapters$29.read(TypeAdapters.java:714)

at com.inductiveautomation.ignition.common.gson.internal.bind.TypeAdapters$29.read(TypeAdapters.java:723)

at com.inductiveautomation.ignition.common.gson.internal.bind.TypeAdapters$29.read(TypeAdapters.java:698)

at com.inductiveautomation.ignition.common.gson.internal.Streams.parse(Streams.java:48)

Darn, less useful than I hoped.

Pretty sure it’s looking for:

[
{key: value}
]

Where my dictionary doesn’t have the array brackets

Seems like this should work, need someone familiar with Perspective to take a look.

The object the jsonGet expression receives isn’t a Document or JSON string value, it’s a java.util.HashMap, which seems wrong to me.

What version is this? The HashMap issue was resolved awhile ago and all objects should be encoded as a DotReferenceJythonMap Regardless, it’s never been easy to pull object values out in expressions, and it’s been so long since I had to do it that I don’t remember how.

Is there a reason you can’t set this up as an Expression Structure?
Then you could store each of the internal keys you need in the structure for easy reference in the expression.

It’s v8.1.0.

I’m not really sure where or why to use an expression structure tbh, they just seem like extra work. In this particular instance, I already have a have a dictionary/object property on the component itself, by duplicating this into an expression structure binding as well, aren’t I just creating extra work when I can just reference the values from the component object? Genuine question there. What are some use-cases where you have used them or seen them used?

I usually see them used when a binding is really dependent on more than one value, as the expression structure binding will update when any of multiple values change. Some users will have simple expression bindings with a script transform which looks at other values at well, but the binding doesn’t update if those secondary values change.

In your case it’s the value of one object, but in essence you’re using more than one value in that object, so the expression structure still makes sense. Also, is there any reason you need to use an expression transform, but can’t use a Script Transform? I’m not saying the Expression Transform can’t be used here, but the Expression Structure Transform would give you access to each internal as a separate value, and the script transform is (personally) easier to use with objects. I very rarely see Expression Transforms used unless a user needs a specific expression function.

The main reason I’ve been using expression bindings the majority of the time is that I figured they might be more efficient to run and therefore increase performance of the server and client. Is this not the case?

Sounds like a benchmark opportunity. If in lieu of a script entirely, I would expect an expression to be faster. I wouldn’t chain an expression and a script, though. Just a script is probably faster than chaining. (Educated guesses…)

I would be very surprised if scripting was anywhere close to as performant as expressions.

2 Likes

Definitely, I try not to chain transforms at all if I can help it, but certainly would use a single script if I need a script.

@Kevin.Herron That's what I assumed :+1:

@nminchin you’re right, this should just work the first way you tried it. Ignore all this noise about hashmaps and json get etc etc. Your {value}['key'] expression should have worked. We’re working on this.

Closing the loop: in tomorrow’s nightly (and thus, 8.1.8), {value}['key'] syntax should work directly for at least the most common types received in Perspective/expressions in general.

4 Likes