1

I have the following json structure returned by an API:

{
    "data": {
        "artemis_ids": {},
        "symbols": {
            "0XBTC": {
                "price": 2.37
            }
        }
    }
}

When I use JSON.parse in Google apps script, the "0XBTC" string is somehow converted to number 11. See below the results of console.log:

console.log("content", content);
console.log("content json", JSON.parse(content));

enter image description here

Any idea why this is happening and how to solve it?

var content = `
    {
        "data": {
            "artemis_ids": {},
            "symbols": {
                "0XBTC": {
                    "price": 2.37
                }
            }
        }
    }
`;

console.log("content", content);
console.log("content json", JSON.parse(content));
user2285985
  • 79
  • 1
  • 6
  • 3
    "*somehow*" - 0xB == 11 – freedomn-m Jul 27 '23 at 15:15
  • You could try encodeURIComponent – ControlAltDel Jul 27 '23 at 15:21
  • 1
    I've added your code as a snippet - it appears to work fine, so must be an issue specifically with [tag:google-apps-script] – freedomn-m Jul 27 '23 at 15:22
  • Note that you have mixed up which is json and which is not - the plain string is *json* - the result of `JSON.parse(json_string)` is a javascript object *not* json. Worth a read: [what is JSON (stack overflow)](https://stackoverflow.com/questions/383692/what-is-json-and-what-is-it-used-for/383699#383699) – freedomn-m Jul 27 '23 at 15:27

2 Answers2

2

Modification points:

From content json {data={artemis_ids={}, symbols={11={price=2.37}}}}, your showing log, I guessed that you might disable the V8 runtime with the script editor. But, when the V8 runtime is disabled, the backticks cannot be used. From this situation, also I guessed that your showing snippet might not be your actual script.

By supposing that my guess is correct, I tested the following sample script. And, I disabled the V8 runtime with the script editor.

function myFunction() {
  var content = '{"data":{"artemis_ids":{},"symbols":{"0XBTC":{"price":2.37}}}}';

  console.log("content", content);
  console.log("content json", JSON.parse(content));
}

When this script is run without V8 runtime, the following result is obtained.

enter image description here

I confirmed that console.log("content json", JSON.parse(content)) shows content json {data={artemis_ids={}, symbols={11={price=2.37}}}}. If my guess is correct, I think that this might be the reason for your current issue.

And also, when I tested the following script.

function sample() {
  var content = '{"data":{"artemis_ids":{},"symbols":{"0XBTC":{"price":2.37}}}}';

  var sample1 = JSON.stringify(JSON.parse(content));
  var sample2 = Object.keys(JSON.parse(content).data.symbols);

  console.log(sample1);
  console.log(sample2);
}

console.log(sample1) and console.log(sample2) are as follows.

{"data":{"artemis_ids":{},"symbols":{"11":{"price":2.37}}}}

and

[11]

From this situation, I guessed that this situation might be a bug or the current specification of Google Apps Script without V8 runtime. But, in the current stage, when the V8 runtime is disabled with the script editor, "runtimeVersion": "DEPRECATED_ES5" is added to the manifest file (appsscript.json). Ref

In order to avoid this situation, as a simple solution, please enable V8 runtime with the script editor. Ref

enter image description here

By this, the above script sample() returns as follows.

{"data":{"artemis_ids":{},"symbols":{"0XBTC":{"price":2.37}}}}

and

[ '0XBTC' ]

0XBTC is correctly used as the key.

Note:

If you are required to use your script by disabling the V8 runtime, how about using polyfill of JSON.parse? Ref When this is reflected in a sample script, it becomes as follows.

function sample2() {
  // Ref: https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse#%E3%83%9D%E3%83%AA%E3%83%95%E3%82%A3%E3%83%AB
  var rx_one = /^[\],:{}\s]*$/;
  var rx_two = /\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g;
  var rx_three = /"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g;
  var rx_four = /(?:^|:|,)(?:\s*\[)+/g;
  var rx_dangerous = /[\u0000\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g;
  JSON.parse = function (text, reviver) {

    // The parse method takes a text and an optional reviver function, and returns
    // a JavaScript value if the text is a valid JSON text.

    var j;

    function walk(holder, key) {

      // The walk method is used to recursively walk the resulting structure so
      // that modifications can be made.

      var k;
      var v;
      var value = holder[key];
      if (value && typeof value === "object") {
        for (k in value) {
          if (Object.prototype.hasOwnProperty.call(value, k)) {
            v = walk(value, k);
            if (v !== undefined) {
              value[k] = v;
            } else {
              delete value[k];
            }
          }
        }
      }
      return reviver.call(holder, key, value);
    }

    // Parsing happens in four stages. In the first stage, we replace certain
    // Unicode characters with escape sequences. JavaScript handles many characters
    // incorrectly, either silently deleting them, or treating them as line endings.

    text = String(text);
    rx_dangerous.lastIndex = 0;
    if (rx_dangerous.test(text)) {
      text = text.replace(rx_dangerous, function (a) {
        return (
          "\\u" +
          ("0000" + a.charCodeAt(0).toString(16)).slice(-4)
        );
      });
    }

    // In the second stage, we run the text against regular expressions that look
    // for non-JSON patterns. We are especially concerned with "()" and "new"
    // because they can cause invocation, and "=" because it can cause mutation.
    // But just to be safe, we want to reject all unexpected forms.

    // We split the second stage into 4 regexp operations in order to work around
    // crippling inefficiencies in IE's and Safari's regexp engines. First we
    // replace the JSON backslash pairs with "@" (a non-JSON character). Second, we
    // replace all simple value tokens with "]" characters. Third, we delete all
    // open brackets that follow a colon or comma or that begin the text. Finally,
    // we look to see that the remaining characters are only whitespace or "]" or
    // "," or ":" or "{" or "}". If that is so, then the text is safe for eval.

    if (
      rx_one.test(
        text
          .replace(rx_two, "@")
          .replace(rx_three, "]")
          .replace(rx_four, "")
      )
    ) {

      // In the third stage we use the eval function to compile the text into a
      // JavaScript structure. The "{" operator is subject to a syntactic ambiguity
      // in JavaScript: it can begin a block or an object literal. We wrap the text
      // in parens to eliminate the ambiguity.

      j = eval("(" + text + ")");

      // In the optional fourth stage, we recursively walk the new structure, passing
      // each name/value pair to a reviver function for possible transformation.

      return (typeof reviver === "function") ?
        walk({
          "": j
        }, "") :
        j;
    }

    // If the text is not JSON parsable, then a SyntaxError is thrown.

    throw new SyntaxError("JSON.parse");
  };


  var content = '{"data":{"artemis_ids":{},"symbols":{"0XBTC":{"price":2.37}}}}';

  var sample1 = JSON.stringify(JSON.parse(content));
  var sample2 = Object.keys(JSON.parse(content).data.symbols);

  console.log(sample1); // <--- {"data":{"artemis_ids":{},"symbols":{"0XBTC":{"price":2.37}}}}
  console.log(sample2); // <--- [0XBTC]
}

sample2();

References:

Tanaike
  • 181,128
  • 11
  • 97
  • 165
  • 1
    Fantastic answer!Your assumption is correct, I'm using Rhino and cannot switch to V8 due to the infamous multiple accounts bug, so I'll go with your polyfill suggestion. Thank you! – user2285985 Jul 28 '23 at 08:00
-1

I think it is not a code issue, because it runs well at node@18 and Chrome@latest.

enter image description here

Finn
  • 101
  • 1
  • 7