JSONPath doesn't produce a valid JSON string?

I’m trying to integrate Frigate with my Openhab instance.
My objective is to collect an event from MQTT, generated by Frigate, using Openhab

I’ve configured a MQTT Generic thing, and added on the channel an item, with a JSONPath profile.

The JSONPath erxpression is:

$.[?(@.type == "new")].after["id", "start_time", "label", "camera", "top_score"]

The raw message is:

{
  "before": {
    "id": "1647960940.191181-xzknx5",
    "camera": "entrance",
    "frame_time": 1647964731.740204,
    "snapshot_time": 1647964730.993641,
    "label": "person",
    "top_score": 0.76953125,
    "false_positive": false,
    "start_time": 1647960940.191181,
    "end_time": null,
    "score": 0.76953125,
    "box": [
      664,
      484,
      725,
      629
    ],
    "area": 8845,
    "region": [
      360,
      303,
      776,
      719
    ],
    "stationary": false,
    "motionless_count": 0,
    "position_changes": 2,
    "current_zones": [
      "path"
    ],
    "entered_zones": [
      "path"
    ],
    "has_clip": true,
    "has_snapshot": true
  },
  "after": {
    "id": "1647960940.191181-xzknx5",
    "camera": "entrance",
    "frame_time": 1647964732.155704,
    "snapshot_time": 1647964730.993641,
    "label": "person",
    "top_score": 0.76953125,
    "false_positive": false,
    "start_time": 1647960940.191181,
    "end_time": 1647964737.264736,
    "score": 0.6953125,
    "box": [
      693,
      521,
      729,
      629
    ],
    "area": 3888,
    "region": [
      548,
      400,
      868,
      720
    ],
    "stationary": false,
    "motionless_count": 1,
    "position_changes": 2,
    "current_zones": [
      "path"
    ],
    "entered_zones": [
      "path"
    ],
    "has_clip": true,             
    "has_snapshot": true              
  },
  "type": "new"
}

The output, read from the item state, is not a valid JSON:
{id=1647960940.191181-xzknx5, start_time=1.647960940191181E9, label=person, camera=entrance, top_score=0.76953125}

So, I’m unable to parse it in my rule (using transform() method with JSONPath can’t work as this is not a valid fragment.

I know I can use the entire message in my rule (and in fact, this is what I’ve done), but it’s not the way I’d like to implement my rule. I’d like my item to only receive the relevant parts from the event, not the whole message.

Should I try another approach or is there a bug hidden here?

So quotes are missing? Seems odd to me…

Did you test the JSONPath Expression with single quotes?

$.[?(@.type == 'new')].after['id', 'start_time', 'label', 'camera', 'top_score']

Both expressions (yours and mine) work the same and produce in theory (tested there: https://jsonpath.herokuapp.com/) a valid JSON object:

[
   {
      "id" : "1647960940.191181-xzknx5",
      "start_time" : 1.647960940191181E9,
      "label" : "person",
      "camera" : "entrance",
      "top_score" : 0.76953125
   }
]

What bother me is the string the Item state returns, which is like an interpreted version of this JSON object, and no longer a valid JSON string.
It shouldn’t contain ‘=’ but ‘:’, double quotes should be almost everywhere, wrapping both attributes names and values.

I had similar issue in a different usecase and decided to use JavaScript transformation instead of jsonpath and parsed the Json within JS.

Maybe that’s also a helpful workaround for you

1 Like

The openHAB JSONPATH Transformation Service is not standard JSONPATH, it has limitations. For example it cannot return objects or arrays, only a single string.

It looks like in transcribing the selected object into a string it is using it’s own format with the =, that’s a shame.

You can play in rules

val rawjson ='{ your data }`
var results = transform("JSONPATH", '$.[?(@.type == "new")].after["id"]', rawjson)
logInfo("test", " simple path to one value: " + results)
results = transform("JSONPATH", '$.[?(@.type == "new")].after["id", "start_time"]', rawjson)
logInfo("test", " returning multi values: " + results)

producing

2022-03-22 22:38:58.140 [INFO ] [.eclipse.smarthome.model.script.test] -  simple path to one value: 1647960940.191181-xzknx5
2022-03-22 22:38:58.142 [INFO ] [.eclipse.smarthome.model.script.test] -  returning multi values: {id=1647960940.191181-xzknx5, start_time=1.647960940191181E9}

which is interesting - with only one element selected, just the value. With multi elements, name and = appear.

As suggested by @Matze0211 by using a JS transform you have direct access to JSON and can build the single return string that you want. It seems a bit pointless though, if you are going to process JSON in a rule again.

Another alternative is to pull out the single elements you are interested in into individual channels/Items, Items are free of cost.

1 Like

I’ve chosen to build a single item containing the whole JSON event message and process it in the rule (using transform() ).

I’ve considered using dedicated items, one per attribute value, but, I wasn’t sure how fast/synchronously they will be updated? As I would read states from each in my rule, I need them to be all updated when the rule is triggered and executed.

EX. :

rule triggered if itemA changed:

read itemA.state
read itemB.state
read itemC.state
do something

if, for any reason, itemB and itemC are not yet updated (even if it’s just a matter of ms), my rule won’t produce what I expect.

You are right, that is a difficult problem to solve in openHAB.
The Items would all get updated from the same MQTT payload, but the updating is one-at-a-time. For a few milliseconds, “first” Item will be “new” value while “last” Item is still “old” value. So there is risk that triggering a rule from one Item can process values from different payloads. You’ve no real control over which is first and last, to choose the best trigger.

If it is not time-critical, a simple 100mS delay at the start of a rule triggered from any Item will ensure everything has “caught up”.

Or there is a clever “synchronise” technique here, its probably beyond what you really need though

Or of course, process the original payload in your rule as a single entity, just as you started out doing.
In that case I would not bother pre-processing it to try to extract a chunk, just work with the whole payload in the rule.