JSonItem cast issue

Hello all;

I have a method that works perfectly fine in the IDE, but fails once compiled. The error is

_VariantString cannot be cast to JSONItem

Here is the method:

// This method will get a distance from the Google Routes API. 

Var LoSource As String = Officiel.Longitude
Var LaSource As String = Officiel.latitude
Var LoDest As String = Site.Longitude
Var LaDest As String = Site.latitude

Var GetDistance as New URLConnection
Var Request as string = "{""origin"":{""location"":{""latLng"":{""latitude"": " + LaSource + ",""longitude"": " + LoSource + "}}},""destination"":{""location"":{""latLng"":{""latitude"":" + LaDest +",""longitude"":" + LoDest + "}}},""travelMode"": ""DRIVE"",""routingPreference"": ""TRAFFIC_UNAWARE"",""computeAlternativeRoutes"": false,""routeModifiers"": {""avoidTolls"": false,""avoidHighways"": false,""avoidFerries"": false},""languageCode"": ""fr-CA"",""units"": ""METRIC""}"

GetDistance.RequestHeader("X-Goog-Api-Key") = APIKeyG.left(APIKeyG.length-2)
GetDistance.RequestHeader("X-Goog-FieldMask") = "routes.distanceMeters"
GetDistance.SetRequestContent(Request, "application/json")
Var Response As String = GetDistance.SendSync("POST", "https://routes.googleapis.com/directions/v2:computeRoutes", 5)
'system.debuglog Response

Var Resp as New JSONItem 
Resp.Load(Response)
Var RespRoutes as New JSONItem 
RespRoutes = Resp.lookup("routes", "Erreur")
Var Route as New JSONItem
Route = RespRoutes.Valueat(0)               ' always select the default (preferred) route

Var Dist as Variant
Dist = Route.value("distanceMeters")

Return Dist.IntegerValue
'System.debuglog Dist.stringvalue

Is this something I am doing wrong, or did I stumble on a bug? It puzzles me somewhat that the same method works fine in the IDE, and fails once compiled.

I am developing on Windows 11, but compiling for Linux X64. This is with 2024 R4.1.

All insights are welcome. Thank you in advance.

LD

Which line is highlighted when you get the RuntimeException?

Edit: Add some debug logging to signal to yourself where the exception is getting raised. The Stack would take us to this code, but it doesn’t tell us whether the Request or the Response is the one being malformed. We need to know whether it’s your request or the response causing this message.

I don’t think that the JSONItems are malformed, since the method does return the correct value when I run the program from the IDE. The error happens only when I run the compiled application on the Linux server.

But yes, I am trying to get as much logging as possible. This is a web application. Remote debugging fails miserably for me between my dev computer and the linux server. That makes it a bit more complicated.

Based on the error, I suspect the error is occurrring here:

Var Dist as Variant
Dist = Route.value(“distanceMeters”)

If Dist is declared as a String instead, I get the same error.(but with string instead of variant)

If you are using Lifeboat you need only Print("My message") to see it in the Web App logs.

Very possible. You should log the response you get to check over the JSON you’re asking Xojo to parse.

I am still not on a public server, and still not using Lifeboat. I will have to do it the hard way.

The error does not seem to occurr where I expected it. I changed Dist to be an Integer (the value is an integer). In the IDE, the method works still just fine, but I ger the casting error regardless.

I will now encapsulate every step in try-catch traps to see where it occurs.

…

I did. The JSON is this:

 {
  "routes": [
    {
      "distanceMeters": 3387
    }
  ]
}

simple enough…

…and the error happens here:

Var Route as New Dictionary
Try
  Route = RespRoutes.Valueat(0)
catch e as runtimeexception
  messagebox "Trap 3 " + e.message
end try

The message is Trap 3 JSONItem is not a array

If I treat RespRoute as not an array, then I get the opposite error. JSONItem is an array. (this is what the code is showing)

I can reproduce the error message:

But this is not a bug; the error is correct.

Why?

This JSON item:

{
  "routes": [
    {
      "distanceMeters": 3387
    }
  ]
}

is not a JSON Array; It is a JSON Object, which contains an Array named “routes”.

To extract the data, you would need to use this syntax:

Var RouteArray as JSONItem
Var Route as JSONItem
RouteArray = RespRoutes.Value("routes")
Route = RouteArray.valueAt(0)

This works, the result:

image

Edit to add: I see now you didn’t include all the code in your second example, so it’s possible RespRoutes may in fact be the Array you extracted? I’m not clear - can you please post a tiny sample project (you can upload a zipped Xojo project file here).

Nevermind my prior response, I think the problem may be here:

Var RespRoutes as New JSONItem 
RespRoutes = Resp.lookup("routes", "Erreur")
Var Route as New JSONItem

If your response fails to include a “routes” object, then Resp.lookup() will not return a JSONItem but rather the string “Erreur” stored in a Variant).

Trying to cast the variant string “Erreur” to a JSONItem will fail exactly as you see:

_VariantString cannot be cast to JSONItem

So as written, your code is not going to properly handle the case when the URLConnection response doesn’t include the “routes” object.

Why is it failing on Linux? Perhaps something about your Linux machine is getting a different response from googleapis ?

You could harden your code a bit by changing the “Erreur” string like this:

Var RespRoutes as New JSONItem 
RespRoutes = Resp.lookup("routes", "[\"distanceMeters\": -999]")

so as to return valid JSON syntax containing an array with an invalid -999 value.

Or even better:

Var RespRoutes as JSONItem 
RespRoutes = Resp.lookup("routes", nil)
if RespRoutes = nil then
   // There is no array of routes, so we need to handle the error condition here
end if

I like your suggestion. Also, the idea that I may not be getting the same response in Linux is plasusible based on what I see.

I will proceed with your suggestion and also I will display the response to have a visual on Linux.

1 Like

Using a -999 value is probably not the best idea, since these hard-coded error values often hurt us later.

FYI, in your code I see this pattern:

Var RespRoutes as New JSONItem 
RespRoutes = ...

notice that you are creating a New JSONItem, then immediately throwing it away in the next line. A little confusing and wasteful.

This could be simplified:

Var RespRoutes as JSONItem = Resp.lookup(... )

@Mike_D , your intuition was correct. I am not getting the same response in the compiled application on Linux:

{
“error”: {
“code”: 400,
“message”: “Invalid JSON payload received. Unexpected token.\natLng":{"latitude": ,"longitude": }}},"d\n ^”,
“status”: “INVALID_ARGUMENT”
}
}

It seems that I am sending the correct JSON data in debugbuild on Windows, but not in the Linux build. Now, I know where to look!

the values for latitude and longitude seem to be missing. That is quite surprising. Same database, same test dataset.

1 Like

So, I traced the issue to another part of the program where Officiel is managed. When not running on my development machine, the incorrect locale would be used and the longitude and latitude data would not be loaded. A very old bug that did not affect much until now.

It had nothing to do with the retrieved JSON ddata, it had everything to do with the correct management of an object created elsewhere in the program.