Detect Payload is JSON object vs. String vs. Array

Upon using MQTT nodes, my testing has run up against a problem which is in the Forum post "MQTT-in node - autodetect string, buffer and JSON object". You can either have the MQTT-in force a JSON object or pass the object in a string. When I use JSON node, it breaks on "Unexpected token in JSON at position 0" when the payload is just a simple string.

Is there a simple, easy means in Javascript function or a node to detect the nature of the Payload, if it is of type Object or String or Array?

(I cannot find any nodes due to the fact that you cannot search by anything other than Node Names in the "flows" tab...)

With the Catch node you can catch the error of JSON parsing but still have the payload

It is very unusual to have something in MQTT that, for the same topic, is of varying types. That suggests a problem with whatever is publishing the data. However the answer to the question is that you can use the typeof keyword for that.
Try sending different things into a function node containing

msg.payload = typeof msg.payload
return msg

and you will see what it gives. Then in a function node you can test the value returned by typeof to decide what to do, you could send the different possibilities to different outputs for example.
If it is a string then you can check whether it may be a JSON object by testing whether it starts and ends with { } and then feed it to a JSON node. That is no guarantee that it is valid json, the only way to know is to parse it.
Really you would be best to sort out whatever is publishing it so it always sends json.

Is this not a Bug, as a string is a valid JSON format?

No, when you set an MQTT In node to parse the input as JSON it expects it to be a string which itself contains valid JSON. If it just accepted any string as JSON then it would never do anything other than pass the string on, even if it contained {"value": 3}

@Colin ,
OK, Yes, I get your point. you should expect from what is being sent to get something expected. The options in the MQTT are...

  1. Auto-detect (string or buffer)
  2. Buffer
  3. String
  4. Parsed JSON object
  5. Base 64 encoded string.

In order to send values they need to be in a context of JSON or a JSON String. if you do not select (4) Parse JSON object, you'll always get a string type for any data 'sent'. That's OK, I guess... If you just send a regular string, it breaks the "JSON" Node.... it supposed to switch from either JSON object or JSON String. It just means that there is more to do to form the data in order to avoid the errors.

If I select (4), I can send numbers, but not strings of NON-JSON String.

If you select (4) you can use the "typeof" in a function. However, that gets you an ERROR if you send the MQTT a regular string of non-JSON format...

In the MQTT-in, is there a means to select the action before you send the data? Else, I need more nodes to send and receive data in various per-determined formats.

It can be too easily misinterpreted

var t = '"t"'
var parsed = JSON.parse(t)
node.warn(parsed)

t = "t"
parsed = JSON.parse(t)
node.warn(parsed)

I would have thought it better to attempt to parse the json string, and if no object or array found, and json is valid format, then the value, number or string, should be assigned to the payload.

@E1cid,
Thank you for your input. YES! it's a matter of programming vs. what is available to do when things do not go as you "expect".

I have seen by testing that I can send timestamp, Numbers and JSON string when selecting a single condition set for the MQTT by "parsed JSON object" ... that is not published by the way... How is this behavior expected? and IF that is possible, then why not be able to evaluate a regular string and pass the results without breaking?

If you can program and have things always work for you and never get unexpected results that can never break your code, congratulations.

Alternately, I'm asking a question on how to manage these things BEFORE I program it. It is really a matter of knowing what I can do about it...

So, if it acts funny its got to be MY bug?

Kind Regards,
@hp_apcc

A string containing just a number or an array or an object are all valid json so can be parsed ok, for example

'4'
'[1,2]'
'{"value": 7}'

whereas a random string is not.
When I suggested using typeof I meant that you could identify string or buffer, but if buffers are not expected then you could set it to string and check the contents for valid data before feeding to a JSON node.
However I still think the best solution is that for any particular topic you make sure the contents is always json.
Alternatively catch the error as suggested earlier, either from the mqtt node, a json node following the mqtt node or in a function node where you call JSON.parse() to convert it and could catch the error inline.
The other thing I don't understand is, if the value may be an object or a string then what are you then doing with it that does not itself generate an error of some sort if it is expecting one but gets the other?

@Colin,
Your saying i should expect to be able to inject a "timestamp" datatype or type "number" data and have it return from the MQTT broker when selected "Parsed JSON object" as number datatype?

That's exactly what the MQTT-in node does.

That is not the same as string containing a number. I'm going to test that too... If it is supposed to work this way, I'm way OK with it. thats just one way for me to pass numbers and not have to put them into JSON objects.

If by timestamp you mean a milliseconds number then yes, not a Date object though.

I think it is the same actually. I think MQTT only handles strings and buffers. The rest is built on top. So if you feed number 1 or string "1" into MQTT they are both sent as strings.
I still don't understand though why one mqtt input node needs to handle both objects and numbers. If it is because you are picking up wildcard topics and they are of different types then use mulitple MQTT In nodes and configure them for the expected type.

@Colin,
I was testing the Nodes to see how it behaves and if I can send complex data or just simple data. I was checking to know how to use it and what I needed to do in order to send via one node with one configuration and changing the MQTT Topic, The topic would also be a factor in determining the data and the datatype. to send it to where ever necessary in the system. So far this is best done by encoding everything in an object to send 'addressing' with the data for switching destination based on the address in the object.

Or different MQTT nodes depending on the what. You have the topics to separate the messages and route them. I'm just seeing how this behaves in order to know best how to use it when sending data to/from 2 or 3 Node-RED instances for one system. These are separated by Security Levels, not necessarily by functions so sharing data is high between these instances of Node-RED. And what to do with unstructured data?

OK, understood.
I am not sure if this is relevant, but generally I would only use wildcards where the result can go through the same flow. If I find myself routing on topic then I would use multiple MQTT in nodes for the different topics rather than routing after picking it up.

@Colin ,
And thus, the answer to this seemingly strange behavior noted with the MQTT-in Node. Thanks for the explanation and bearing with me through this.

Kind Regards,
@hp_apcc

@hotNipi ,
Thanks for the JSON. syntax. This is what I needed to use to compare and manage unexpected information in non-JSON strings. I'll look a bit more into the JSON object for programming.

Thanks,
@hp_apcc

The JSON object is both highly useful and a pain in the neck. Given that most JSON processing doesn't support comments and is very picky about structure (no trailing comma's for example), you do have to be careful when processing it.

When using JSON.parse or JSON.stringify for example, always wrap the statement in a try {} catch (e) {} to prevent it from crashing your process.

Yeeeah!, I was going over the JSON.parse() and there is this "reviver" parameter... reading it put me to sleep.

Not that hard really, it performs a transform and/or filter on each key/value pair that has been parsed out of the string and before it is added to the output object.

For example, you could enforce a maximum value or filter out keys whose value was a specific word or phrase.

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