Forum Discussion

marting's avatar
marting
Icon for Neophyte rankNeophyte
2 years ago

JSON Path capability in a Webpage DataSource

I think the answer to this is gonna be “You need to script it, dummy”, but figured I’d check anyway...

I'm working on a new DataSource that pulls/interprets JSON data from a peudo-custom system via HTTP.

The system has a status page that lists the status of various components using JSON elements that have this general format:

ParameterName&ParameterType

Initial idea was that I could use the Webpage collector since it supports JSON/BSON parsing.

Issue I’m running into is that the values on most of these JSON elements are string (i.e. “true”/”false”).

I set up a DataPoint that can extract that value by putting in the JSON Path like so:

$.[##WILDVALUE##].[ParameterName&ParameterType]

...and I can see that I’m getting the “true”/”false” values back when I do a Poll Now.  But - as we know - LM won’t deal with strings natively.

Workaround I came up with was to get length of the string since true/false are different lenghts.

According to sources online, JSON Path should support a calculation of string length.

I've also verified that I can do this by pasting my data and my JSON Path expression:

$.[ComponentName].[ParameterName&ParameterType].length

...into https://jsonpath.com/

In this parser, the .length function works as expected, and returns the length of the JSON value.

However, in LogicMonitor, I'm just getting this (example failure):

NAN after processing with json
(postProcessParam: $.[ComponentName].[ParameterName&ParameterType].length, reference=, useValue=body)

Anyone know if there is a way to make this JSON Path length function work?

  • Anonymous's avatar
    Anonymous
    2 years ago

    There’s an easier way than this:

    function parseBooleanValue(value) {
    if (value === 'true') {
    return 1;
    } else if (value === 'false') {
    return 0;
    } else {
    return null; // or throw an error, depending on how you want to handle invalid values
    }
    }

    It uses the ternary operator:

    (value == "true") ? 1 : 0

    This is exactly equivalent to:

    if (value == "true") {
    return 1
    } else {
    return 0
    }

    No need for a function at all, it’s overkill.

  • Anonymous's avatar
    Anonymous

    There’s an easier way than this:

    function parseBooleanValue(value) {
    if (value === 'true') {
    return 1;
    } else if (value === 'false') {
    return 0;
    } else {
    return null; // or throw an error, depending on how you want to handle invalid values
    }
    }

    It uses the ternary operator:

    (value == "true") ? 1 : 0

    This is exactly equivalent to:

    if (value == "true") {
    return 1
    } else {
    return 0
    }

    No need for a function at all, it’s overkill.

  • Hi @marting --

    I’m thinking you might actually need to script this to where you’re mapping the expected string values (true/false) to integer values, such as --

    function parseBooleanValue(value) {
    if (value === 'true') {
    return 1;
    } else if (value === 'false') {
    return 0;
    } else {
    return null; // or throw an error, depending on how you want to handle invalid values
    }
    }

    Can’t fully vouch if this script will work specifically for you, but maybe something like this for some ideas --

    // Define the URL of the JSON data source
    def url = "https://example.com/status.json"

    // Define the JSON Path expressions to extract the data
    def expressions = [
    "component1" : [
    "param1&string" : "$.components[0].params[0].value",
    "param2&bool" : "$.components[0].params[1].value"
    ],
    "component2" : [
    "param1&string" : "$.components[1].params[0].value",
    "param2&bool" : "$.components[1].params[1].value"
    ]
    ]

    // Define a function to parse boolean values
    def parseBooleanValue(value) {
    if (value === 'true') {
    return 1;
    } else if (value === 'false') {
    return 0;
    } else {
    return null;
    }
    }

    // Define the main data collection function
    def collectData() {
    // Fetch the JSON data from the URL
    def response = get(url)

    // Parse the JSON data
    def json = new JsonSlurper().parseText(response.text)

    // Loop through the components and parameters
    for (componentName in expressions.keySet()) {
    for (paramKey in expressions[componentName].keySet()) {
    // Extract the value from the JSON data using the JSON Path expression
    def value = JsonPath.read(json, expressions[componentName][paramKey])

    // Convert boolean values to integers
    if (paramKey.endsWith("&bool")) {
    value = parseBooleanValue(value)
    }

    // Create a new datapoint with the value
    def dpName = "${componentName}_${paramKey.replaceAll("&", "_")}"
    def dp = new DataPoint(dpName, value)
    addDataPoint(dp)
    }
    }
    }

    // Call the main data collection function
    collectData()

    In the above boilerplate, you would of course need to replace the url variable with the URL of your JSON data source and modify the expressions variable to match the structure of your JSON data source.

    I hope this helps to stir some new ideas for you!

  • Thanks for the input @Isaac Paramo and @Stuart Weenig.  Going to give this a go in the near future.