makeWebRequest returns an Array, even though the documentation states it can not.

As per this thread:

 WebRequest: How to ignore wrong response content type? 

The callback function for makewebrequest has the type:

responseCallback as Lang.Method(responseCode as Lang.Number, data as Lang.Dictionary or Lang.String or PersistedContent.Iterator or Null) as Void or Lang.Method(responseCode as Lang.Number, data as Lang.Dictionary or Lang.String or PersistedContent.Iterator or Null, context as Lang.Object) as Void

So it supposedly doesn't return data as Lang.Array, even that is what the response is. The only proposed workaround is to treat the response as plain text, instead of as JSON object. But I managed to get an Array:

function callback(responseCode as Number, data as Dictionary or String or PersistedContent.Iterator or Null, c as Object) as Void {
  if (responseCode == 200 and data instanceof Array) {
    System.println(data.size()); // executed correctly, even though compiler warns about this line being unreachable

  }

}

I use Communications.HTTP_RESPONSE_CONTENT_TYPE_JSON as reponseType, I can see in the debugger, that the data argument is indeed an Array. 

Is this behavior reliable? I tried it in a simulator, not on a real watch yet.

Perhaps this is a new, undocumented feature in the SDK, not present on older firmwares?

  • Interesting observation. What does the array contain? Is the root element of your JSON response an array?

    As far as I know, the root of a JSON document can be an array, and in that case it would make sense for the API to return an array as well. Maybe this is simply a documentation gap.

  • Yes, the root is an Array. As in:

    [{„id”: 1, „name”: „a”}, {„id”:2, „name”: „b”}]

    This is Toggl Tracks API, returning the list of projects to track by the way. I’m trying to build an app for time tracking with Toggl Track.

    I get an Array of Dictionaries, so it works just I would like it to, even though JSON shouldn’t have an array as outermost entity, as I recall from my webdev days.

    Also, I see another two year old post about the same:

     How to receive Lang.Array type on makeWebRequest 

    I’m still worried about which firmwares this actually works with, how long it is going to work. But if it has been working for 2 years now, I guess I’ll use it.

  • My 2 cents:

    - Since at least 2013, valid JSON is any serialized value: null, boolean, string, number, array, or object

    - As per the linked thread in the OP, at least in 2022 (and earlier), Connect IQ required a JSON response to be an object, which caused lots of problems with devs accessing valid JSON endpoints which returned something other than an object

    The body is a valid JSON (string).

    The result you show is a valid JSON value, but we expect a valid JSON object. Yes, many web service APIs return JSON values (string, number, array, object true, false, or null), but we require the result to be a JSON object if you tell use the response content type is JSON.

    As you have discovered, we will provide direct access to the content as a String if you can convince the VM to accept the content as TEXT.

    It's possible things have changed since then. As Gabor pointed out, in 2024 someone found that they can receive an array as a JSON response in Connect IQ.

    If it were me, my main concern would be that old devices whose firmware hasn't been updated in years wouldn't be the beneficiary of this change. Of course that would only be a problem if I wanted to support old devices.

    even though JSON shouldn’t have an array as outermost entity, as I recall from my webdev days.

    The first JSON RFC, released in 2006 as an informational document, allowed for JSON to be an object or an array. This was superseded in 2013, by a standards-track RFC which redefined valid JSON as any serialized value.

    https://en.wikipedia.org/wiki/JSON#Data_types

    Early versions of JSON (such as specified by RFC 4627) required that a valid JSON text must consist of only an object or an array type, which could contain other types within them. This restriction was dropped in RFC 7158, where a JSON text was redefined as any serialized value.

    Try out https://jsonlint.com/

  • Oh, yes, I was wrong, array should be okey as a JSON response. So It is Garmin that is weird here.

    So, scrolling through the about section on my watch, it says 

    "This product may contain JSMN, distributed under the JSMN license"

    Hence, I think they use this library for parsing JSON:

    https://github.com/zserge/jsmn

    I assume, this means they didn't create their own parser,

    I boldly assume, that they always used this parser,

    Thus I boldly this feature (parsing array as expected) was always available, but I might check the release logs of JSMN.

    If I ever publish this app, I just don't publish it for too old devices (older than 4 years?), then if someone complains about a crash during this makewebrequest execution, I can withdraw support for that device. I surely won't put more effort into an app I make for free, in my own time : )

  • Thus I boldly this feature (parsing array as expected) was always available, but I might check the release logs of JSMN.

    My guess was always that the "JSON must be an object" behaviour wasn't a technical limitation, but it was done so that the data of the response in Connect IQ would always be a Monkey C dictionary, to make things simpler.

    That's just a guess though.

    JSMN has been around for 16 years, so it would be interesting to see what it considered valid JSON in 2010 (I'm just gonna guess that it's the same as in 2026.)

  • The 2nd version of JSMN's readme, from Nov 2010, says:

    github.com/.../README

    The rudimentary jsmn object is a **token**.

    When parsing is done, token objects contain start and end positions of JSON
    token inside the JSON data block. You can just copy a corresponding range of
    bytes and get token value.

    Another propetry of token is token type. It describes the type of the
    corresponding JSON object.

    jsmn supports the following token types:

    * Object - a container of key-value pairs, e.g.:
    `{ "foo":"bar", "x":0.3 }`
    * Array - a sequence of values, e.g.:
    `[ 1, 2, 3 ]`
    * String - a quoted sequence of chars, e.g.: `"foo"`
    * Primitive - a number, a boolean (`true`, `false`) or `null`

    So I stand by my guess that the CIQ behaviour is/was a deliberate design on the CIQ end, and not anything to do with the library

  • If the JSON represents array data, you’ll get an array. It’s worked this way forever  

    You want the array since there’s no practical way of doing that yourself.

  • If the JSON represents array data, you’ll get an array. It’s worked this way forever  

    I don't think Connect IQ has worked this way forever, unless "forever" means "since some point around maybe 2024, and definitely no earlier than 2022".

    As explained above, there was a time (such as 2022 and earlier) where CIQ functions like makeWebRequest would return an error for a non-object JSON response when the response type was specified as JSON. CIQ goes above and beyond typical HTTP APIs in the sense that it tries to validate the response body against the response type, and completely suppresses the response if there's a mismatch. I think most APIs would just give you the response anyway, and let you deal with it. (I get that part of the reason here is that makeWebRequest handles the conversion of the JSON response to a Monkey C object such as a dictionary, so it really does need valid JSON. The point of contention here is that what makeWebRequest accepts or accepted as valid JSON is a subset of what's actually valid JSON.)

    In other words, most APIs seem to follow the robustness principle: "be conservative in what you send, be liberal in what you accept"

    While Connect IQ, at least when it comes to makeWebRequest, goes against that principle in being very conservative about what it accepts (or at least it used to be that way).

    I quoted the relevant response from a Connect IQ team member which was posted in 2022 above:

    That response was in the thread that was linked in the OP.

    This has actually caused lots of problems in the past, for people who used JSON endpoints that were returning things other than an object. In the past, the suggestion was to either specify the response type as text and parse the response yourself (if possible), or implement your own proxy which wraps the JSON response in an object.

    Regardless of the historical or current behaviour, the OP also correctly points out that the type information for makeWebRequest does not indicate that the returned data can be an array, which would indeed be a problem if the returned data can in fact be an array.

    It seems to me that the historical forum record, historical device behaviour, and the current type information indicate that it has not worked this way forever. If not for any of those things, this discussion would probably not exist.

  • As explained above, there was a time (such as 2022 and earlier) where CIQ functions like makeWebRequest would return an error for a non-object JSON response when the response type was specified as JSON.

    Err. You are correct: it stubbornly expected an object. I now recall complaining about that years ago. (I might not have quite been in the right mind when I posted above).

    This has actually caused lots of problems in the past, for people who used JSON endpoints that were returning things other than an object. In the past, the suggestion was to either specify the response type as text and parse the response yourself (if possible), or implement your own proxy which wraps the JSON response in an object.

    Yes, this (newish) behavior is a big improvement.

    It (at least) opens up being able to use data sources you cannot change (you might not own them). The “past suggestions” kind of wasted a lot of people’s time.

    The only possible reasonable explanation for the old behavior was to save memory space. Outside of that, the old behavior would have been just a bad choice).

    Parsing the text yourself is not good for a bunch of reasons. At the least, it would have very likely taken much more code than the more flexible (and correct) current behavior would have.

    In other words, most APIs seem to follow the robustness principle: "be conservative in what you send, be liberal in what you accept"

    This principle really applies to “incorrect” input.

    It looks the old JSON standard was objects or arrays. So, the old IQ behavior was (technically) always a bug (not a failure not being “liberal in accepting”).