1. Sverre Tvedt
  2. PowerBuilder
  3. Tuesday, 9 April 2019 14:06 PM UTC

(PB 2017 R3 build 1858 / Windows 10 )

From an httpclient object I receive JSON strings  that I need to parse.  They may be quite complicated with deeply nested structures.

My first problem is to parse NULL values.

My preliminary code looks like this:

 

ls_result = ij_parser.loadstring(as_json)
ll_root = ij_parser.getrootitem( )
//
...
this.ErrorDetails = ij_parser.getitemstring(ll_root,"ErrorDetails")

this.ErrorMessage= ij_parser.getitemstring(ll_root,"ErrorMessage")

this.Successful= ij_parser.getitemboolean(ll_root,"Successful")

...

and so on for the various elements. I know the expected datatypes, of course.

However, there may be a null or more in the dataset, like this:

"Successful":true,"ErrorMessage":null,"ErrorDetails":null

 

This code causes runtime exceptions, rather than returning null strings. Why? Buggy parser? I would have expected null strings to be returned.

To avoid it I have to wrap every getitem in aTRY-CATCH block , and do a setnull in the catch part. Yikes.

Is there a better way without going completely low-level?

 

Accepted Answer
Chris Pollach @Appeon Accepted Answer Pending Moderation
  1. Tuesday, 9 April 2019 16:20 PM UTC
  2. PowerBuilder
  3. # Permalink

Hi Sverre;

   In PowerBuilder 2019 coming soon at the end of May, you will have some new enhancements to the JSON Parser object, as follows:

1) New "ContainsKey" command

  • Allows you to verify if the "Key" exists before you ask the parser to go after a NULL key / value.

2) New "GetItemType" command

  • Allows you to check the JSON data type before issuing a GetItemXxxxx() command

3) New "ReturnsNullWhenError" setting

  • Allows you get a NULL back instead of throwing an exception

HTH

Regards ... Chris

 

Comment
  1. Sverre Tvedt
  2. Thursday, 11 April 2019 12:04 PM UTC
Kevin,

I can successfully get an array of objects by using the JSONPackage.GetValue, and a small homemade function to seperate the items in the square bracket enclosed result. That is enough to pass the individual item strings on to the parser method of the subordinated object(s). In this way any object in the hierarchy only has to deal with keys on its own level. The price to pay is to write a parser method in every data contract nvo.



Then finally there is the last (?) problem, boolean values. They are not strings, so there is no JSONPackage primitive to access them. Seems I have to put a JSONParser object in my wrapper object as well to do this.









JSONPackage.GetValue("Successful") causes runtimeerror whatever value, false or true.

I have abandoened the idea of making wrapper object around a JSONPackage instance, it is currently not designed for general parsing of JSON strings. Seems I must use same approach, but based on the JSONParser. Maybe I need both.



regards sverre

  1. Helpful
  1. Kevin Ridley
  2. Thursday, 11 April 2019 12:29 PM UTC
It's posted but shows up below this reply I guess because this reply was flagged as the solution. Hope it works for you.
  1. Helpful
  1. Kevin Ridley
  2. Thursday, 11 April 2019 13:24 PM UTC
Sverre,

Unfortunately for the boolean, you're going to have to use JSONParser. The JSONPackage doesn't support boolean and apparently null either. These are both bugs, although the boolean would probably be an enhancement request. You will have to use the JSONParser for this one unfortunately. Here's some code to get the value for the Successful key:

boolean lb_success

string ls_json

long ll_root



jsonparser ljp_parse



try

ljp_parse = create jsonparser

ls_json = "the json you posted"

ljp_parse.Loadstring( ls_json )



ll_root = ljp_parse.GetRootItem()

lb_success = ljp_parse.getitemboolean(ll_root, "Successful")

MessageBox("", String(lb_success))

catch(runtimeerror re)

MessageBox("RuntimeError", re.getmessage())

finally

IF IsValid(ljp_parse) THEN

destroy ljp_parse

END IF

end try
  1. Helpful
There are no comments made yet.
Kevin Ridley Accepted Answer Pending Moderation
  1. Thursday, 11 April 2019 12:25 PM UTC
  2. PowerBuilder
  3. # 1

Sverre,

 I think I might have a solution for your complex parsing and nulls issues (and maybe a new best friend, lol).  Attached find some sample code.  Spent a couple hours playing around with this last night and think I have it to where it's usable with only minor modifications.  What I did was pull out the json items using the JSONPackage object as we discussed before.  Then when you get to the embedded array, you just slap that into a datastore or dw.  I know you said you didn't want to use a dw, but this is where I think your null issue goes away.

 

So here's what to do when you run my code:

Paste your original json into the top mle.  Extract the first level by putting the key "Users" into the Get Value sle and click the Parse button.  This extracts the Users json.  Then copy/paste that back into the top mle, replacing the current full json.  BE SURE to delete the brackets [] around (obviously you will have to do this in code to automate) or you will get a runtime error.  Type "Profiles" into the Get Value sle and click the Parse button again.  This will give you the Profiles array json.  Now click the Load button and poof you have your Profiles array parsed into a datawindow (or ds more likely in code) with your empty string and nulls handled!  Should be easy to loop through the dw/ds to get your values for the data contract objects.  If they attributes are arrays, you can even pull the whole column at once into the array with dot notation.  Hope this solves your issues.

 

Cheers,

Kevin

https://www.linkedin.com/in/kevin-ridley-88a40913/

Attachments (1)
Comment
  1. Kevin Ridley
  2. Thursday, 11 April 2019 14:08 PM UTC
If you don't want to use dws you will have to use the parser object to get the individual data elements. IMO the dw is easier and eliminates your null issue, but it's obviously up to you. It also gives you the advantage of being able to create an array directly from a column if your data object's attribute is an array, as I mentioned before. No silver bullet here but you have some options, including possible tweaks to the business processing that you were trying to avoid by keeping those data contract objects. Since you will probably never go back to SOAP it might be time to give it a look. Good luck.
  1. Helpful
  1. Sverre Tvedt
  2. Friday, 12 April 2019 12:29 PM UTC
The conclusion now is that your solution in fact is the only possible one!

I have alternatively tried using the JSONParser methods to get the individual attributes, combined with the JSONPackage to single out compundobjects/arrays, for downward processing.

This works very well and with sufficiently small additions to the data contract nvos. As long as there are no null values! They cause exceptions that for some strange reason cannot be properly handled by TRY/CATCH and after a certain number of caught exceptions the application just freezes. The JSONParser is sadly in a too early stage of development, and the JSONPackage is too closely tied to datawindows. Looks like several un-coordinated hands have been at work here.
  1. Helpful
  1. Kevin Ridley
  2. Wednesday, 17 April 2019 17:22 PM UTC
  1. Helpful
There are no comments made yet.
Kevin Ridley Accepted Answer Pending Moderation
  1. Tuesday, 9 April 2019 15:43 PM UTC
  2. PowerBuilder
  3. # 2

Hi Sverre,

 It's tough to diagnose without the JSON you're trying to parse, but will take your word for it.  You should probably open a bug, but you will probably have to send the JSON so they can reproduce it.  You'll have to check for any personal info in it and maybe change some values to protect the info unless it's true test data.

 

That being said, have you tried using the JSONPackage object?  I've had a lot of success using that to pull values, especially nested JSON objects (GetValue()).  You can often pull out the nested objects and load them into a dw/ds using ImportJSON.  Maybe a combination of those can get you where you need to be.  Might be worth a shot till they can fix the JSONParser if it's a bug.

 

1 other thing, be sure you put the JSON through a JSON validator to make sure you're actually parsing valid json.

 

Kevin

Comment
  1. Sverre Tvedt
  2. Tuesday, 9 April 2019 18:39 PM UTC
Thanks for advice, Kevin!

The JSON string comes directly from our trusted business partner, serving many other customers.

I have considered the datawindow approach, but it won't work. What I do is to migrate a very large Soap client project with something like a hundred complicated data contract objects in/out. As you probably know, the serializing/deserializing to/from XML in Soap is fully automatic, and all the data contract objects and proxy functions are automatically generated. Following a call to a proxy function, one has immediately the deserialized return contract objects.

For various reasons I am forced to migrate from Soap to some other hybrid web service solution.

I do not want to re-write the next upper tier in the application to deal with datawindows. Takes too much time and is pretty risky.

What I do is that I keep all the interface objects from the Soap version and replace the calls to the proxy functions

with

(1) serializing the inherited Soap input objects to JSON

(2) make a post with httpclient

(3) deserialize the return JSON-string into the original soap return objects.

With this approach, all code above this application tier is unchanged.



Now, and this is a bit worse: We are consuming many Soap services from public institutions. They are forcing us over time to support TLS 1.2. This means effectively, since Appeon hesitates to support TLS 1.2 in .NET soap client projects, we will have to move to the same httpclient approach, but in these cases using low-level serailizing/deserializing of XML in place of JSON, We lose the automatic handling of XML. Lots of work. The charm of the good old EasySoap/.NET Soap client projects in which you could consume a web service in minutes is gone.

  1. Helpful
  1. Daryl Foster
  2. Wednesday, 10 April 2019 05:52 AM UTC
Hi Sverre,



The serializing and de-serializing of XML is the best part of Powerbuilder's soap implementation and it will be a shame to lose it. We had a similar problem when a web service we used moved to using MTOM for their response which Powerbuilder couldn't handle. Our solution was to write a relay in PHP which then becomes our endpoint for the web service. The PHP relay takes the request and passes it to the real endpoint (using cURL) and then modifies the MTOM response to send back to the client as plain xml. It all works seamlessly and the XML serializing/de-serializing still works in the client. The only change we had to make was to change the web service endpoint in our client application.



You could possibly do something similar to get around your TLS 1.2 issues if you write a relay (in the language of your choice) which takes the request from the Powerbuilder app, passes it to the web service using TLS 1.2 and then sends the response back to your client. We used PHP because we use that internally and it was easy for us to set up an endpoint on our internal servers.
  1. Helpful
  1. Sverre Tvedt
  2. Wednesday, 10 April 2019 07:52 AM UTC
Great suggestion, Daryl! In the days of EasySoap, we had sometimes troubles with complex data types that EasySoap couldn't handle, and the solution would be some variant of a relay method. It has a cost side, in that one has to hire a specialist. Not to mention the additional nuisance of handling updates. After the initial children's diseases of PB .NET Soap projects, it now performs wonderfully. Soap servers will be around for many years still, in my case with the added restriction of TLS 1.2. The enhancement to 1.2 on the PB client side is in my regular evening prayer.
  1. Helpful
There are no comments made yet.
  • Page :
  • 1


There are no replies made for this question yet.
However, you are not allowed to reply to this question.