Search for data in msg. payload based on values within payload. Is this possible?

Hi,

I'm using node red for a while now as rule engine for my home automation system. Now I've stumbled over a problem, I cannot solve by myself. Every idea how to solve is highly welcome :slight_smile:

The problem is hard to explain, because I'm not a native English speaker, but I try to...

I try to get information about my heating system with a Rest-API call and want to use the received information (as object) in a flow. This works so far but...

When requesting e.g. "outside temperature" from my heating system the received msg-object looks like this (shortened):

{
    "_msgid": "b4d8608.3e462a",
    "payload": {
        "properties": {
            "value": {
                "value": 8.2,
                "type": "number"
            },
            "status": {
                "value": "connected",
                "type": "string"
            },
            "unit": {
                "value": "celsius",
                "type": "string"
            }
        },
        "path": "properties.value.value"
    },
    "statusCode": 200
}

So the value of msg.payload.path tells me where to find the value I want to extract. In this example it is at msg.payload.properties.vale.value

Another example when asking the REST-API e.g. for the "outside temperature" is:

{
    "_msgid": "5f35fd23.ced484",
    "payload": {
        "properties": {
            "active": {
                "value": true,
                "type": "boolean"
            },
            "demand": {
                "value": "unknown",
                "type": "string"
            },
            "temperature": {
                "value": 22,
                "type": "number"
            }
        },
        "path": "properties.temperature.value"
    },
    "statusCode": 200
}

Here the value of msg.payload.path tells me that the value I want to extract is at msg.payload.properties.temperature.value

Is it possible, maybe with a change node and JSONata, to first get the value of msg.path and use this value as path to the value I want to extract??? How???

I played a bit with JSONata but with our success :frowning:

Regards,
Michael

Welcome to the forum @NoMercyMike.

In a function node this is one way to do it

let value = eval(`msg.payload.${msg.payload.path}`)

but I don't like using eval. I suspect there is a better way using something like
msg.payload[msg.payload.path]
but that doesn't work.

Yes the same way as in function node with $eval
e.g.

[{"id":"932dafe1.4093b8","type":"inject","z":"8d22ae29.7df6d","name":"","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{\"properties\":{\"value\":{\"value\":8.2,\"type\":\"number\"},\"status\":{\"value\":\"connected\",\"type\":\"string\"},\"unit\":{\"value\":\"celsius\",\"type\":\"string\"}},\"path\":\"properties.value.value\"}","payloadType":"json","x":120,"y":2500,"wires":[["e8ea619b.7e60a","f98b95cf.92cb"]]},{"id":"e8ea619b.7e60a","type":"change","z":"8d22ae29.7df6d","name":"","rules":[{"t":"set","p":"payload","pt":"msg","to":"$eval(\"payload.\" & payload.path)","tot":"jsonata"}],"action":"","property":"","from":"","to":"","reg":false,"x":330,"y":2500,"wires":[["f98b95cf.92cb"]]},{"id":"f98b95cf.92cb","type":"debug","z":"8d22ae29.7df6d","name":"Na Template","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":620,"y":2300,"wires":[]}]

The way I would do it is to use the JSONata $eval(...) function inside a change node, with this expression:

payload.$eval(path)

I tried the $lookup(...) function, but that only appears to search 1 level, not the 3 listed in the 'path' string...

1 Like

Without eval

var path = msg.payload.path.split(".")
while(path.length){
    msg.payload = msg.payload[path.shift()]
}
return msg;

I like that way. Eval is dangerous as if you get something unexpected, or even malicious, in path it will execute it.

It is just a quick and dirty example. If path is incorrect it fails pretty hard. So needs improvements for real usage.

You can use the builtin helper function RED.util.getMessageProperty ...

getMessageProperty(msg,expr)
msg is the object to "get" a value from
expr is the path to the item

so instead of passing msg in, pass msg.payload in.

FUNCTION CODE...

msg.value = RED.util.getMessageProperty(msg.payload, msg.payload.path);
return msg;

Demo...

[{"id":"2a6df415.f905fc","type":"inject","z":"1ce23005.82287","name":"fake it","props":[{"p":"payload"},{"p":"topic","vt":"str"}],"repeat":"","crontab":"","once":false,"onceDelay":0.1,"topic":"","payload":"{     \"properties\": {         \"active\": {             \"value\": true,             \"type\": \"boolean\"         },         \"demand\": {             \"value\": \"unknown\",             \"type\": \"string\"         },         \"temperature\": {             \"value\": 22,             \"type\": \"number\"         }     },     \"path\": \"properties.temperature.value\" }","payloadType":"json","x":380,"y":780,"wires":[["158d361d.bd639a"]]},{"id":"158d361d.bd639a","type":"function","z":"1ce23005.82287","name":"","func":"debugger\nmsg.value = RED.util.getMessageProperty(msg.payload, msg.payload.path);\nreturn msg;","outputs":1,"noerr":0,"initialize":"","finalize":"","x":530,"y":780,"wires":[["e5e22824.4b89c8"]]},{"id":"e5e22824.4b89c8","type":"debug","z":"1ce23005.82287","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"true","targetType":"full","statusVal":"","statusType":"auto","x":690,"y":780,"wires":[]}]
2 Likes

Many thanks @all :slight_smile:

At least the solution from Steve-Mci worked for me. Great!!!

True, I always try to avoid using eval in Javascript... but I'm less worried about it inside a JSONata expression, where the data is all sand-boxed and there is no access to the file system.

This topic was automatically closed 14 days after the last reply. New replies are no longer allowed.