Don't let the new JSON Object trip you up

Two new JSON functions arrived in v22.

  1. JSONParse
  2. JSONParsedState

When using JSON previously it has been stored as a string. With the arrival of v22 using the JSONParse function will store your JSON internally as a JSON object. *

This is great for performance when you have large amounts of data. The benefits with small amounts of JSON are negligible.

However, if you are accustomed to sharing your JSON and you expect the output to be a string (just is it was in versions less than v22) then you will need to ensure that you are not passing a string and not the new JSON Object Type. Serializing the objects will keep your PHP dev happy.

The simplest method for serializing the JSON objects is to concatenate with an empty string.

"" & json --> json as a string

The other method is to extract the JSON using JSON functions

Set Variable [ $json ; JSONSetElement ( "" ; "name" ; $jsonObject ; jsonString )

* It is possible to access this behaviour in v21 using JSONGetElement ( json ; "" ). This is effectively the same as calling JSONParse. Credit goes to @steve_ssh for observing this in the wild.

9 Likes

DoesGetAsText(json) do the right thing?

1 Like

Yes, GetAsText() will do the trick.

If you need to test the state of json being stored by FileMaker use JSONParsedState ( json ). If it returns zero it is a string. If it returns -1 it is not valid json. Otherwise it returns a number that corresponds to the type of object that the JSON represents, such as array, object, boolean, etc.

3 Likes

Can you provide an example of when you might need to explicitly string-ify a parsed JSON object? i.e. when would you need to "" & $json or GetAsText($json)?

I understood FM to automatically stringify json as needed, without explicit coercion.

My understanding is that parsed JSON is stored as both a string and internally as a the new JSON object type. But this should not change anything when sending JSON to another destination outside of FIleMaker. Exporting, sending via Insert From URL, etc. should all receive a string as usual.

Do you have an example of something specific that breaks?

Also note that if you build your JSON (or modify JSON) using the native JSONSetElement or view it with JSONFormatElement, those functions will also produce “parsed” JSON, so you don’t need to separately call JSONParse. You can test this with JSONParsedState.

3 Likes

POST-ing JSON to a PHP page for consumption.

The scripts had been producing a string formatted as JSON that was included as data in the POST header. The JSON was built on the fly and optionally included a JSON object that was passed in as a parameter. It started failing and our web dev showed me what he was receiving. In the place we expected to see something like this "configdata":{"studentID":"687678","programID”: "GCFRGYGFYCGFP"} instead we saw JSON OBJECT. I modified our script to concatenate the json with an empty string and things went back to normal.

2 Likes

This sounds like a serious, and unintentional bug.

Just to be clear, was it something like this?

Set Variable [ $json ; JSONSetElement ( "{}" ; "configdata.studentID" ; "12345" ; JSONString ]

// reference $json var directly in curl options using `@` syntax:
Set Variable [ $curl ; "-X POST -d @$json" ]

Insert From URL [ url: "https://my-endpoint.php" ; curl: $curl ]

EDIT: I just used ngrok to test the above and it’s working as expected. I wonder do you have plug-ins involved for doing the POST request, e.g. BaseElements?

EDIT 2: I just tested BaseElements BE_HTTP_POST and that didn’t produce “JSON OBJECT” either in the received payload. I can’t seem to reproduce this–I’m very curious how @Malcolm got it to do that.

// BaseElements example
Set Variable [ $data ; Value: JSONSetElement ( "{}" ; 	[ "hello" ; "world" ; JSONString ] ) ]
// this returns "3" (JSONObject):
Set Variable [ $$PARSED_STATE ; Value: JSONParsedState ( $data ) ]
Set Variable [ $url ; Value: "https://my-test-endpoint" ]

Set Variable [ $r ; Value: BE_HTTP_SetCustomHeader ( "Content-Type" ; "application/json" ) ]
Set Variable [ $r ; Value: BE_HTTP_POST ( $url ; $data ) ]

1 Like

This is what we are doing.

Insert from URL [ Select ; With Dialog: Off ; Target: $result ; $url ; cURL options : $options ]

$options looks like this.

-X POST 
--header "Content-Type: application/json" 
--header "X-API-AUTH: XYZ" 
--header "moe: 1234" 
--user-agent IDMA 
--data-binary @$data 
--dump-header $responseHeaders

We’re passing the JSON through in the $data and we’re declaring that the data is binary, so I guess that we’ve been hoist on our own petard.

1 Like

Man, I can’t reproduce the issue you described at all in FMP 22.01. I did:

  1. pass parsed state 0 (plain string, unparsed) as --data
  2. pass parsed state 0 (plain string, unparsed) as --data-binary
  3. pass parsed state 3 (JSONObject) as --data
  4. pass parsed state 3 (JSONObject) as --data-binary

I copied those curl options verbatim.

I suspect something else was causing the issue, unrelated to the FMP side.

I think what @flybynight said is basically correct, that FMP can hold either parsed json, a string, or both in the new DBData object that represents JSON in FMP variables. Then, whenever FM needs a stringified version, it’ll create and store it as needed, even just for something as simple as displaying the JSON in the data viewer. Though I do think it’s good to try to suss out edge case bugs where they may exist. For example I did find and report one related to the data viewer unparsing parsed objects when you flip between the “Current” and “Watch” tabs.

Best,J

2 Likes