XMLtoJSON policy rounds long numbers

An XMLtoJSON policy with <RecognizeNumber>true</RecognizeNumber> will round long numbers causing unexpected issues.

In our application we use 18-digit codes. We have an XMLtoJSON policy in place which rounds the codes and replaces the last 2 digits with 00:

Example:

871689213001765046 becomes:

871689213001765000

Solved Solved
1 2 805
1 ACCEPTED SOLUTION

I understand what you've written, and.. .I think that the XMLToJSON policy is working correctly, and that the problem you are seeing is the result of an additional subtlety that you are not considering.

Here's why I think this.

I just built a simple loopback test proxy that includes just a few policies:

  • AssignMessage - to assign a simple XML payload to a message
  • XMLToJSON - to convert that to JSON
  • AssignMessage - to inject the output JSON into the response message

...and it works as expected. Please find it attached here. apiproxy-xml2json-recognizenumber.zip

The input XML is

<a>
  <foo>871689213001765046</foo>
</a>

The XMLToJSON policy config is:

<XMLToJSON name='XMLToJSON-1'>
  <Source>testMessage</Source>
  <OutputVariable>output_json</OutputVariable>
  <Options>
    <RecognizeNumber>true</RecognizeNumber>
  </Options>
</XMLToJSON>

The output JSON is:

{"a":{"foo":871689213001765046}

This is exactly as expected.

So what's going on in your case? I don't know, but maybe the problem is, you are evaluating the resulting JSON in a node.js program or JavaScript callout. Are you?

You may be aware: JavaScript uses a double-precision number format, which you can read about here. There are no types for regular "integer" or "long integer" or even "floating point". Every number instantiated in a JavaScript context has the same type, Number, and each Number follows the IEEE 754 standard. This in-memory storage format allows a limited precision. It's not possible to represent an arbitrarily large integer in that format, and the specific number you described causes an "rounding error" in javascript.

Here's an example. When I invoke "node" it launches an interactive session in which I can run JavaScript code.

$ node
> var j = Number(871689213001765046);
undefined
> j
871689213001765000
> 

Or, if you would like to match your situation (parsing JSON) more closely:

$ node
> var s = JSON.parse('{"a":{"foo":871689213001765046}}');
undefined
> s.a.foo
871689213001765000
> 

You can see in both cases the original value of the number gets rounded, to the same figure you cited.

With this experiment, I am ready to guess that the problem you are experiencing is not in the XML-to-JSON conversion as done by the Apigee Edge policy, but in the evaluation (parsing) of the resulting JSON in a JavaScript environment.

Let me know if this is consistent with your own tests.

If this is the problem, then.. I suggest you try one of these workarounds or avoidances:

  1. turn off RecognizeNumber and then the policy will treat that string of digits as a string.
  2. parse the JSON in an environment (maybe a Java callout?) that can handle a double-precision or LONG integer.

View solution in original post

2 REPLIES 2

I understand what you've written, and.. .I think that the XMLToJSON policy is working correctly, and that the problem you are seeing is the result of an additional subtlety that you are not considering.

Here's why I think this.

I just built a simple loopback test proxy that includes just a few policies:

  • AssignMessage - to assign a simple XML payload to a message
  • XMLToJSON - to convert that to JSON
  • AssignMessage - to inject the output JSON into the response message

...and it works as expected. Please find it attached here. apiproxy-xml2json-recognizenumber.zip

The input XML is

<a>
  <foo>871689213001765046</foo>
</a>

The XMLToJSON policy config is:

<XMLToJSON name='XMLToJSON-1'>
  <Source>testMessage</Source>
  <OutputVariable>output_json</OutputVariable>
  <Options>
    <RecognizeNumber>true</RecognizeNumber>
  </Options>
</XMLToJSON>

The output JSON is:

{"a":{"foo":871689213001765046}

This is exactly as expected.

So what's going on in your case? I don't know, but maybe the problem is, you are evaluating the resulting JSON in a node.js program or JavaScript callout. Are you?

You may be aware: JavaScript uses a double-precision number format, which you can read about here. There are no types for regular "integer" or "long integer" or even "floating point". Every number instantiated in a JavaScript context has the same type, Number, and each Number follows the IEEE 754 standard. This in-memory storage format allows a limited precision. It's not possible to represent an arbitrarily large integer in that format, and the specific number you described causes an "rounding error" in javascript.

Here's an example. When I invoke "node" it launches an interactive session in which I can run JavaScript code.

$ node
> var j = Number(871689213001765046);
undefined
> j
871689213001765000
> 

Or, if you would like to match your situation (parsing JSON) more closely:

$ node
> var s = JSON.parse('{"a":{"foo":871689213001765046}}');
undefined
> s.a.foo
871689213001765000
> 

You can see in both cases the original value of the number gets rounded, to the same figure you cited.

With this experiment, I am ready to guess that the problem you are experiencing is not in the XML-to-JSON conversion as done by the Apigee Edge policy, but in the evaluation (parsing) of the resulting JSON in a JavaScript environment.

Let me know if this is consistent with your own tests.

If this is the problem, then.. I suggest you try one of these workarounds or avoidances:

  1. turn off RecognizeNumber and then the policy will treat that string of digits as a string.
  2. parse the JSON in an environment (maybe a Java callout?) that can handle a double-precision or LONG integer.

Thank you for the clear answer, that explains our problem exactly.