Community discussions

MikroTik App
 
User avatar
Amm0
Forum Guru
Forum Guru
Topic Author
Posts: 3509
Joined: Sun May 01, 2016 7:12 pm
Location: California

$JSON - parsing JSON to/from array type

Tue Sep 19, 2023 10:11 pm

I have a wrapper script around @winand'sJSON parser that make it a little more friendly to use, which is combined with "str2json" part based on @rextended's helpers:


The way the wrapper works is looking at the first argument.
If it's a string, it's assumed you want JSON converted into a RouterOS array (e.g. parse something from ([/tool/fetch...]->"data").
If it's array, then it assume you want JSON as a string (e.g. submitting to website via /tool/fetch)
### $JSON <str | array>
#   if first arg is RouterOS array type, it will convert to JSON as string
#   if arg is string, it's assumed it's JSON, so parsed into RouterOS array
#   note: nothing will be printed by default, so it must be used without something like :put [$JSON $myarray]
:global JSON
:set JSON do={
    :global JSONLoads
    :put [:typeof $JSONLoads]
    :if ([:typeof $JSONLoads]="nothing") do={
        # can comment out once file has been download, but will be downloading only once here anyway
        /tool fetch url=https://raw.githubusercontent.com/Winand/mikrotik-json-parser/master/JParseFunctions
        :import JParseFunctions
        :delay 5s
    }
    :if ([:typeof $1]="str") do={
        :local got
        :if ([len [/file find name=$1]] > 0) do={
            :global JSONLoads
            :set $got [$JSONLoads [/file get $1 contents]]
            :return $got
        }
        :set $got [$JSONLoads $1]
        :return $got
    }
    :if ([:typeof $1]="array") do={
        :local tojson do={
            :local ret "{"
            :local firstIteration true
            :foreach k,v in=$1 do={
                if ($firstIteration) do={
                :set $ret ($ret . "\"".$k . "\":")
                } else={
                :set $ret ($ret . "," . "\"". $k . "\":")
                };
                :set $firstIteration false
                :local type [:typeof $v]
                :if ($type = "array") do={
                :set $ret ($ret . [$tojson $v])
                } else={
                :if ($type = "str" || $type = "id" || $type = "time" || $type = "ip" || $type = "ipv6") do={
                    :set $ret ($ret . "\"" . $v . "\"")
                } else {
                    :set $ret ($ret . $v )
                };
                };
            };
            :set $ret ($ret . "}")
            :return $ret;
        };
        :return [$tojson $1]
    }
    :error "Bug: Unhandled code path"
}
NOTE: It will automatically download the script and load it, but you can comment out those lines and use it directly.


Also, sometime the request needs to be URLEncoded. RouterOS has a built-in v7.12 to do this. But for completeness here, here is some lightly-modified, but now legacy @rextended code that does it. This takes a RouterOS string as first argument to function, and then returns the URLencoded string for potential use in building a URL for /tool/fetch url="...":
# from https://forum.mikrotik.com/viewtopic.php?p=670983&hilit=urlencode#p885685
:global URLENCODE
:set URLENCODE do={
    :local Chars {" "="%20";"!"="%21";"#"="%23";"%"="%25";"&"="%26";"'"="%27";"("="%28";")"="%29";"*"="%2A";"+"="%2B";","="%2C";"/"="%2F";":"="%3A";";"="%3B";"<"="%3C";"="="%3D";">"="%3E";"@"="%40";"["="%5B";"]"="%5D";"^"="%5E";"`"="%60";"{"="%7B";"|"="%7C";"}"="%7D"}
    :set ($Chars->"\07") "%07"
    :set ($Chars->"\0A") "%0A"
    :set ($Chars->"\0D") "%0D"
    :set ($Chars->"\22") "%22"
    :set ($Chars->"\24") "%24"
    :set ($Chars->"\3F") "%3F"
    :set ($Chars->"\5C") "%5C"
    :local URLEncodeStr
    :local Char
    :local EncChar
    :for i from=0 to=([:len $1]-1) do={
        :set Char [:pick $1 $i]
        :set EncChar ($Chars->$Char)
        :if (any $EncChar) do={
            :set URLEncodeStr "$URLEncodeStr$EncChar"
        } else={
            :set URLEncodeStr "$URLEncodeStr$Char"
        }
    }
    :return $URLEncodeStr
}

Finally if RouterOS array to YAML output is needed, see viewtopic.php?p=1025335 . This becomes handy to VIEW JSON on RouterOS in more understand manner. So you can chain them for output
:put [$YAML [$JSON ($fetchResults->"data")]
If there are newer innovations here ... feel free to comment or offer alternative implementations below. Just posting some code I've used, not saying it's great — just want post to reference for some JSON encode/decode all-in-one function.

Who is online

Users browsing this forum: No registered users and 3 guests