XML to JSON policy data conversion issue, 0123 not being treated as an integer

Not applicable

Am i missing something here?

I'm experiencing an issue with data conversion using the XML to JSON policy .

When <RecognizeNumber>true</RecognizeNumber> is set in options of XML to JSON policy

Suppose my input xmlData is

<abc>
<Number>0123</Number>
</abc> 

This is converted to

{
  "abc":  {
    "Number": "0123"
  }
} 

But when Number is not starting with 0 say 1234 the output json is

{
  "abc":  {
    "Number": 1234
  }
}

When <RecognizeNumber>false</RecognizeNumber> then the policy always treats it as a string .

My requirement is to make the xml to json policy always treat it as a Number. How do i do it ?

1 1 1,709
1 REPLY 1

Hmmm, ok thanks for the clear question.

YES, you are missing something.

The JSON spec is pretty clear about how to represent a number. It gives this diagram explaining the logic:

source: http://www.json.org/number.gif

The way I interpret that, a string beginning with 0 followed by several other digits, is not a number.

These are all numbers:

  • 0
  • -0
  • -1
  • 0.0
  • 0.123

But unfortunately for you, 0123 is not a valid representation of a number.

The converter in Apigee Edge converts the "0123" always to a string, which is correct behavior.

Note: the JSON spec does not rely on the behavior of the parseInt() function in JavaScript, or any other JavaScript function, to determine whether a string is a number. The JSON format is defined independent of any language or platform. So it is possible for JavaScript's parseInt() to return a number, while the JSON serializer may treat the thing as a string.

You can work around this, if in your case you want strings like "01234" to be converted to numbers.

To do so generally, for any property in the JSON hash, you'd need a JavaScript callout to walk the resulting JSON graph, and convert strings that can be converted with parseInt(). If you know EXACTLY which property needs to be interpreted as a number, you don't need to walk the graph, but could do it more quickly.

This is what the walk-the-graph approach looks like:

var intRegex = new RegExp('^-?[0-9]+$');
function maybeStringToInt(c){
  var t = typeof c;
  if (t == 'string' && intRegex.test(c)) {
    return parseInt(c, 10);
  }
  return c;
}
var source = { "abc": { "Number": "0123" } }; 
// or alternatively 
// var source = JSON.parse(context.getVariable('request.content');
console.log('before: ' + JSON.stringify(source));
walkGraph(source, maybeStringToInt);
console.log('after: ' + JSON.stringify(source));
// context.setVariable('request.content', JSON.stringify(source));

The above relies on a function like this:

var what = Object.prototype.toString;


function walkGraph(obj, converterFn) {
  var wo = what.call(obj);


  if (wo == "[object Object]") {
    Object.keys(obj).forEach(function(key){
      var thing = obj[key];
      if (what.call(thing) == "[object Object]" || what.call(thing) == "[object Array]") {
        walkGraph(thing, converterFn);
      }
      else {
        obj[key] = converterFn(obj[key]);
      }
    });
  }


  else if (wo == "[object Array]") {
    obj.map(function(currentValue, index, array){
      if (what.call(currentValue) == "[object Object]" || what.call(currentValue) == "[object Array]") {
        walkGraph(currentValue, converterFn);
      }
      else {
        array[index] = converterFn(currentValue);
      }
    });
  }
  
  else {
    var msg  = "Unknown object to covert: " + wo + "("+ JSON.stringify(obj, null, 2).slice(0, 34) +")";
    console.log(msg);
    throw new Error( msg );
  }
}

This is what the convert just one well known property approach looks like:

var source = { "abc": { "Number": "0123" } }; 
// or alternatively 
// var source = JSON.parse(context.getVariable('request.content');
console.log('before: ' + JSON.stringify(source));
source.abc.Number = parseInt(source.abc.Number,10);
console.log('after: ' + JSON.stringify(source));
// context.setVariable('request.content', JSON.stringify(source));