Subscribe for automatic updates: RSS icon RSS

Login icon Sign in for full access | Help icon Help
Advanced search

Pages: [1]
  Reply  |  Print  
Author Topic: Searching a JSON array  (Read 8240 times)
Paul M.
Posts: 16


« on: February 03, 2022, 07:30:21 pm »

I have a program that reads in a nested JSON string  like below:
{"Value":[{"DomainId":23,"DomainName":"PU","Description":"Public Utilities","WebTitle":"","ImageUrl":"","EditButtonsOn":false,"MapServiceId":1,"MobileMapCacheId":0},{"DomainId":21,"DomainName":"PW","Description":"Public Works","WebTitle":"","ImageUrl":"","EditButtonsOn":false,"MapServiceId":2,"MobileMapCacheId":0}],"Status":0,"Message":null,"ErrorMessages":[],"WarningMessages":[],"SuccessMessages":[]}

I set up a bdl record to catch this:

mycatch   RECORD
   Value DYNAMIC ARRAY of util.JSONObject ,       # util.JSONArray
   Status STRING,
   Message STRING,
   ErrorMessages DYNAMIC ARRAY OF STRING,
   WarningMessages DYNAMIC ARRAY OF STRING,
   SuccessMessages DYNAMIC ARRAY OF String
  END RECORD


In the code  I can do.

for i = 1 to mycatch.value.getlength()
  display mycatch.value.get("DomainId")," - ", mycatch.value.get("DomainName")
END FOR

but when I ask
let i = mycatch.value.search("DomainId","PU")

it returns 0

util.JSONArray does not have a search function, so I thought a dynamic array of JSONObjects would work. 
Am I missing something simple? 

I know I can substring the JSON array part of the string and push it into a dynamic array, but that seems like over kill.

Any ideas are appreciated.
Sebastien F.
Four Js
Posts: 545


« Reply #1 on: February 04, 2022, 08:20:11 am »

Hello,

The ARRAY.search() method does not apply to util.JSONObject properties.

I would expect a runtime error if you use the search() method on a DYNAMIC ARRAY OF <class> (looks like a bug)

If your JSON data has a static structure, you can define the complete RECORD with all fields of a "Value" element, and then use the ARRAY.search() method:

Code
  1. IMPORT util
  2.  
  3. MAIN
  4.   DEFINE s STRING
  5.   DEFINE mycatch RECORD
  6.       --Value DYNAMIC ARRAY of util.JSONObject,
  7.       Value DYNAMIC ARRAY OF RECORD
  8.            DomainId INTEGER,
  9.            DomainName STRING,
  10.            Description STRING,
  11.            WebTitle STRING,
  12.            ImageUrl STRING,
  13.            EditButtonsOn BOOLEAN,
  14.            MapServiceId INTEGER,
  15.            MobileMapCacheId INTEGER
  16.       END RECORD,
  17.       Status STRING,
  18.       Message STRING,
  19.       ErrorMessages DYNAMIC ARRAY OF STRING,
  20.       WarningMessages DYNAMIC ARRAY OF STRING,
  21.       SuccessMessages DYNAMIC ARRAY OF String
  22.      END RECORD
  23.  
  24.   LET s = '{"Value":[{"DomainId":23,"DomainName":"PU","Description":"Public Utilities","WebTitle":"","ImageUrl":"","EditButtonsOn":false,"MapServiceId":1,"MobileMapCacheId":0},{"DomainId":21,"DomainName":"PW","Description":"Public Works","WebTitle":"","ImageUrl":"","EditButtonsOn":false,"MapServiceId":2,"MobileMapCacheId":0}],"Status":0,"Message":null,"ErrorMessages":[],"WarningMessages":[],"SuccessMessages":[]}'
  25.  
  26.   CALL util.JSON.parse(s, mycatch)
  27.  
  28.   DISPLAY util.JSON.format(util.JSON.stringify(mycatch))
  29.  
  30.   DISPLAY mycatch.value.search("DomainName","PU")
  31.  
  32. END MAIN
  33.  


You wrote:

Quote
In the code  I can do.

for i = 1 to mycatch.value.getlength()
  display mycatch.value.get("DomainId")," - ", mycatch.value.get("DomainName")
END FOR

This cannot work if mycatch.value is a DYNAMIC ARRAY, there is no such method like get():

Code
  1. sf@toro:/tmp$ fglcomp -M search.4gl && fglrun search.42m
  2. search.4gl:33:25:33:27:error:(-8447) function 'get' not found in type 'base.Array'.
  3. search.4gl:33:62:33:64:error:(-8447) function 'get' not found in type 'base.Array'.
  4.  

See:

https://4js.com/online_documentation/fjs-fgl-manual-html/#fgl-topics/c_fgl_Arrays_ARRAY_methods.html

I assume you have mixed up your code, trying different types util.JSONArray / util.JSONObject / DYNAMIC ARRAY OF util.JSONObject ...

Note also that util.JSONArray.get() takes an index as argument, not a property name as when using util.JSONObject.get():

https://4js.com/online_documentation/fjs-fgl-manual-html/#fgl-topics/c_fgl_ext_util_JSONArray_get.html

https://4js.com/online_documentation/fjs-fgl-manual-html/#fgl-topics/c_fgl_ext_util_JSONObject_get.html


If the JSON data structure is variable, you can define the Value member as a util.JSONArray and then write generic code to search properties in the JSON structure.

If this is the case, send a request to the support channel and we'll follow up and provide a sample.

Seb
Leo S.
Four Js
Posts: 129


« Reply #2 on: February 04, 2022, 11:16:17 am »

You are not forced btw to have all attributes/data structures in your target record.
The util.JSON.parse function is smart and only fills the matching members.
So if you aren't interested in some details of the JSON string transmitted you can simply omit them in your target RECORD structure.
The snippet below is working too.

Code
  1. IMPORT util
  2. MAIN
  3.  DEFINE s STRING
  4.  DEFINE mycatch RECORD
  5.    Value DYNAMIC ARRAY OF RECORD
  6.      DomainId INT,
  7.      DomainName STRING,
  8.      Description STRING
  9.      --...etc
  10.    END RECORD
  11.  END RECORD
  12.  LET s='{"Value":[{"DomainId":23,"DomainName":"PU","Description":"Public Utilities","WebTitle":"","ImageUrl":"","EditButtonsOn":false,"MapServiceId":1,"MobileMapCacheId":0},{"DomainId":21,"DomainName":"PW","Description":"Public Works","WebTitle":"","ImageUrl":"","EditButtonsOn":false,"MapServiceId":2,"MobileMapCacheId":0}],"Status":0,"Message":null,"ErrorMessages":[],"WarningMessages":[],"SuccessMessages":[]}'
  13.  CALL util.JSON.parse(s,mycatch)
  14.  DISPLAY mycatch.Value.search("DomainName","PU")
  15. END MAIN
  16.  
Regards, Leo
Leo S.
Four Js
Posts: 129


« Reply #3 on: February 04, 2022, 11:20:32 am »

Ah, and I wanted to add that you should always
try
Code
  1. DISPLAY util.JSON.proposeType(s)
  2.  
to ease your work.
it would have displayed
Code
  1. RECORD
  2.    Value DYNAMIC ARRAY OF RECORD
  3.        DomainId FLOAT,
  4.        DomainName STRING,
  5.        Description STRING,
  6.        WebTitle STRING,
  7.        ImageUrl STRING,
  8.        EditButtonsOn BOOLEAN,
  9.        MapServiceId FLOAT,
  10.        MobileMapCacheId FLOAT
  11.    END RECORD,
  12.    Status FLOAT,
  13.    Message STRING,
  14.    ErrorMessages DYNAMIC ARRAY OF STRING,
  15.    WarningMessages DYNAMIC ARRAY OF STRING,
  16.    SuccessMessages DYNAMIC ARRAY OF STRING
  17. END RECORD
  18.  
Sebastien F.
Four Js
Posts: 545


« Reply #4 on: February 04, 2022, 11:38:37 am »

About ARRAY.search(), in fact you don't get a runtime error because you can search for object references:

Code
  1. MAIN
  2.    DEFINE c1, c2 base.Channel
  3.    DEFINE a DYNAMIC ARRAY OF base.Channel
  4.    LET c1 = base.Channel.create()
  5.    LET c2 = base.Channel.create()
  6.    LET a[1] = NULL
  7.    LET a[2] = c1
  8.    LET a[3] = NULL
  9.    LET a[4] = c2
  10.    DISPLAY a.search("", c1)
  11.    DISPLAY a.search("", c2)
  12.    DISPLAY a.search("", "foo")
  13. END MAIN

Then:
Code
  1. $ fglcomp ch.4gl && fglrun ch.42m
  2.          2
  3.          4
  4.          0

I have some doubts about the behavior when passing a field name, thought:

Code
  1. DISPLAY a.search("xxxxxx", c1)

That one could raise a runtime error IMO.
Pages: [1]
  Reply  |  Print  
 
Jump to:  

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines