Force float value when using JSON.stringify

I want to force a Number to be a Float, after JSON.stringify(). Unfortunately JSON.stringify() deletes the 1 .0.

Example :

JSON.stringify(1.0) // "1"

Wanted outcome:

JSON.stringify(1.0) // "1.0"

I'm using an API which wants an object in JSON-Format, but it only understands decimal values. So I wanted to ask if it is possible using JSON.stringify to generate the string with decimal values without using Regex-.replace-magic


Solution 1:

The straight and reasonably short answer to your question is no, you cannot customize the serialization of numeric values with JSON.stringify. See JSON.stringify() on MDN.

If you really need decimals for your integers you have to use "regex-magic" as you mention in your question or find a library allowing you to perform such tricks. If you aren't dealing with overly complex or many different kinds of objects you could probably stringify them manually.

As a sidenote, it sounds very, very suspicious with an API that needs to be fed with custom formatted JSON. I would tripple-check if there are any other way of using it.

Solution 2:

Use toFixed instead of stringify. Example:

var num = 1;
var numStr = num.toFixed(1); //result is "1.0"

More about toFixed - http://www.w3schools.com/jsref/jsref_tofixed.asp.

To be clear We are talking about conversion number to string not number to float ( such type not exists in javascript ) like can be understood from question description. toFixed will always return String with specified number of decimals.

Solution 3:

Well for anyone wondering about the regex magic here it is: This is taken from stringify-with-floats npm library but modified to support any length of floats.

const beginFloat = "~begin~float~";
const endFloat = "~end~float~";

const stringifyWithFloats =
  (config = {}, decimals = 1) =>
  (inputValue, inputReplacer, space) => {
    const inputReplacerIsFunction = typeof inputReplacer === "function";
    let isFirstIteration = true;
    const jsonReplacer = (key, val) => {
      if (isFirstIteration) {
        isFirstIteration = false;
        return inputReplacerIsFunction ? inputReplacer(key, val) : val;
      }
      let value;
      if (inputReplacerIsFunction) {
        value = inputReplacer(key, val);
      } else if (Array.isArray(inputReplacer)) {
        // remove the property if it is not included in the inputReplacer array
        value = inputReplacer.indexOf(key) !== -1 ? val : undefined;
      } else {
        value = val;
      }
      const forceFloat =
        config[key] === "float" &&
        (value || value === 0) &&
        typeof value === "number" &&
        !value.toString().toLowerCase().includes("e");
      return forceFloat ? `${beginFloat}${value}${endFloat}` : value;
    };
    const json = JSON.stringify(inputValue, jsonReplacer, space);
    const regexReplacer = (match, num) => {
      return num.includes(".") || Number.isNaN(num)
        ? Number.isNaN(num)
          ? num
          : Number(num).toFixed(decimals)
        : `${num}.${"0".repeat(decimals)}`;
    };
    const re = new RegExp(`"${beginFloat}(.+?)${endFloat}"`, "g");
    return json.replace(re, regexReplacer);
  };

const test = {
  test: "test",
  float: 1.2,
  number: 1,
};

const formatted = stringifyWithFloats(
  { float: "float", number: "float" },
  2
)(test, null, 2); // null and 2 is just for pretty print

console.log(formatted);