Welcome to our new forum
All users of the legacy CODESYS Forums, please create a new account at account.codesys.com. But make sure to use the same E-Mail address as in the old Forum. Then your posts will be matched. Close

JSON (WString format) accessing elements. Convert to STRUCT?

2021-11-15
2023-05-23
  • justthefacts77 - 2021-11-15

    Hey Folks,

    I am obtaining data from a Weather API site using the "WEB_CLIENT" block (HTTP Get). JSON data comes in and is stored off in a "WSTRING" as follows:

    "{"coord":{"lon":-82.4584,"lat":27.9475},"weather":[{"id":804,"main":"Clouds","description":"overcast clouds","icon":"04d"}],"base":"stations","main":{"temp":72.48,"feels_like":71.6,"temp_min":68.05,"temp_max":76.15,"pressure":1023,"humidity":43},....."

    Pretty format...

    {
    "coord": {
    "lon": -82.4584,
    "lat": 27.9475
    },
    "weather":
    {
    "id": 804,
    "main": "Clouds",
    "description": "overcast clouds",
    "icon": "04d"
    }
    ,
    "base": "stations",
    "main": {
    "temp": 66.96,
    "feels_like": 65.61,
    "temp_min": 63.95,
    "temp_max": 70.41,
    "pressure": 1023,
    "humidity": 48
    },....

    ?- What is the optimal way to access elements in the Array.

    Seems like It be converted into a STRUCT and elements pulled with the "dot . " convention i.e.
    MAIN.TEMP = 66.96

    Regards

     
  • tvm - 2021-11-16

    Take a look at this library: https://forge.codesys.com/lib/pro-json/home/Home/
    I created it and did most of the testing with open weather map, which looks like what you're using as well.

     
  • justthefacts77 - 2021-11-16

    Thank you much TVM!

    My biggest challenge is to parse out an element from an Array, so i was thinking multiple steps:
    - Find Key for the Array location
    - Find item in Array via an Index lookup...etc.

    Anyway ill dig into it.

    I am looking at the JSON Utilies SL library right now but hampered by the fact my HTTP Client is outputting a WString (JSON) from the weather API and haven't figured out way to feed into the FBlks they offer.

    Thank you again!

     
  • tvm - 2021-11-16

    well, for what it's worth, the JSON library uses STRING variables as well, but feel free to modify it.
    The main conversion function blocks uses a pointer to the string:

    JSONString:     POINTER TO ARRAY[1..GPL_JSON.MAX_JSON_STRING] OF BYTE;
    

    It might be as easy as changing the BYTE to a WORD, because after that it uses array indexes, not pointer math. But I haven't tested that.

     

    Last edit: tvm 2021-11-17
  • justthefacts77 - 2021-11-17

    Ok so looking at "ParseJSON"...feed it in a String, then how do I access members of an Element, then an object in an Array, i.e. how does it determine which Type?

    "coord" : {

    "lon" : -82.4584,
    "lat" : 27.9475
    

    },
    "weather" : [

    {
    "id" : 804,
    "main" : Clouds,
    "description" : overcast clouds,
    "icon" : 04d
    }

    ],

    ?- What would be the syntax to assign the element to a temp variable, for example
    temp := JSONVars.weather.main

    Appreciate any input, gonna have to do my due diligence on the coding aspects for sure.

     
  • tvm - 2021-11-17

    Here's an example of one of the structures I was using with Open Weather Map:

    TYPE OWM_WEATHER :
    STRUCT
        id:             JSONVAR;
        main:           JSONVAR;
        description:    JSONVAR;
        icon:           JSONVAR;
    END_STRUCT
    END_TYPE
    
    Weather:    OWM_WEATHER;
    

    You'll have to create structures and arrays that match the expected result from whichever API you're using. Order doesn't matter, but the structure does. (But parsing will be faster if the order matches as closely as possible).

    So then, for example, if I want to get the description, I could access Weather.description.CharString
    or if I wanted the id, I'd use Weather.id.Integer, (or Weather.id.Number if I wanted it as a floating point value)

    take a look at the documentation in the JSONVAR function block, basically it guesses at the type when parsing from the string (because it uses the setter of the .AsString property), but you can then access it as whatever type you need (string, boolean, integer, number, or datetime)

     
    • aott33 - 2023-03-30

      Thanks for creating this library.

      I need some assistance troubleshooting why I am getting null values. I have followed your directions and the parse json example is not working me. I found this thread and thought it would be a good place to post my question as it is a similar application/issue to mine. I am receiving a String Value from an MQTT subscribe function block. The issue is that I am getting null values.

      Below is my struct:

      TYPE FanRefSpeedJson :
      STRUCT
          timestamp       : JSONVAR;
          value           : JSONVAR;
      END_STRUCT
      END_TYPE
      

      Below are the relevant variables:

      VAR
          sPayload                : STRING;
          FanRefSpeedJSON_1       : FanRefSpeedJson;
          ParseJSON               : JSON_TO_STRUCT;
      END_VAR
      

      Below is the return value from the MQTT Subscribe Function Block (sPayload)

      '{"timestamp":1680099744985,"value":30}'
      

      Below is the ParseJSON code:

      ParseJSON(
          Execute:= TRUE,
          JSONString:= ADR(sPayload), 
          JSONStringSize:= SIZEOF(sPayload),
          JSONVars:= ADR(FanRefSpeedJSON_1),
          NumberOfVars:= SIZEOF(FanRefSpeedJSON_1) / SIZEOF(JSONVAR)
      );
      

      Attached are some screenshots showing the code and null values.

       

      Last edit: aott33 2023-03-30
  • tvm - 2023-03-30

    I just tried your code here and it works for me. ParseJSON is looking for the rising edge of Execute, are you sure your sPayload variable has data in it before the parse is executed?

     

    Last edit: tvm 2023-03-30
    • aott33 - 2023-03-30

      That makes sense. I will do some testing and update you here. Thanks for pointing me in the right direction.

       
      • aott33 - 2023-03-31

        Thanks, TVM! I used the xReceived output from the Subscribe function block to trigger the ParsJSON function and it worked!

         
        πŸ‘
        1
        • kdasystems - 2023-05-21

          Hello, can you share the code from the funcional block jsonvar. when I go to it is empty, only I can see the vars.
          Actually I followed all conversation and at my site it doesnt work

           
          • tvm - 2023-05-23

            There's not actually any code in the JSONVAR function block itself, all the work is done in properties and methods. If you post an example I can test it.

             

Log in to post a comment.