JSON.stringify without quotes on properties?

JavascriptJson

Javascript Problem Overview


I'm using a service which uses incorrect JSON format (no double quotes around properties). So I need to send

{ name: "John Smith" } instead of { "name": "John Smith" }

This format cannot be changed as this is not my service.

Anyone know of a stringify routing to format an JavaScript object like above?

Javascript Solutions


Solution 1 - Javascript

This simple regular expression solution works to unquote JSON property names in most cases:

const object = { name: 'John Smith' };
const json = JSON.stringify(object);  // {"name":"John Smith"}
console.log(json);
const unquoted = json.replace(/"([^"]+)":/g, '$1:');
console.log(unquoted);  // {name:"John Smith"}

Extreme case:

var json = '{ "name": "J\\":ohn Smith" }'
json.replace(/\\"/g,"\uFFFF");  // U+ FFFF
json = json.replace(/"([^"]+)":/g, '$1:').replace(/\uFFFF/g, '\\\"');
// '{ name: "J\":ohn Smith" }'

Special thanks to Rob W for fixing it.

Limitations

In normal cases the aforementioned regexp will work, but mathematically it is impossible to describe the JSON format with a regular expression such that it will work in every single cases (counting the same number of curly brackets is impossible with regexp.) Therefore, I have create a new function to remove quotes by formally parsing the JSON string via native function and reserialize it:

function stringify(obj_from_json) {
	if (typeof obj_from_json !== "object" || Array.isArray(obj_from_json)){
    	// not an object, stringify using native function
    	return JSON.stringify(obj_from_json);
    }
    // Implements recursive object serialization according to JSON spec
    // but without quotes around the keys.
    let props = Object
     	.keys(obj_from_json)
        .map(key => `${key}:${stringify(obj_from_json[key])}`)
        .join(",");
    return `{${props}}`;
}

Example: https://jsfiddle.net/DerekL/mssybp3k/

Solution 2 - Javascript

It looks like this is a simple Object toString method that you are looking for.

In Node.js this is solved by using the util object and calling util.inspect(yourObject). This will give you all that you want. follow this link for more options including depth of the application of method. http://nodejs.org/api/util.html#util_util_inspect_object_options

So, what you are looking for is basically an object inspector not a JSON converter. JSON format specifies that all properties must be enclosed in double quotes. Hence there will not be JSON converters to do what you want as that is simply not a JSON format.Specs here: https://developer.mozilla.org/en-US/docs/Using_native_JSON

Object to string or inspection is what you need depending on the language of your server.

Solution 3 - Javascript

TL;DR -- The Code

With a caveat, below, about lookbehind support in browsers, here you go:

function cleanIt(obj) {
    var cleaned = JSON.stringify(obj, null, 2);

    return cleaned.replace(/^[\t ]*"[^:\n\r]+(?<!\\)":/gm, function (match) {
        return match.replace(/"/g, "");
    });
}

> NOTE: Lookbehinds aren't supported on IE or, strangely, any version of Safari as of 18 June 2021. To use there, remove (?<!\\) from the regex and watch out for the "gotcha" condition mentioned below.

That stringifies with prettiness, uses our regex to find keys, then each key match is replaced via a replacer function that says "here's your match with all double quotes removed". Since we only matched keys up to their colons, that's exactly what we want.

Detailed description below.


Why the accepted answer is not all right

Unfortunately, as Adel points out points out, there's a pretty serious issue with Derek's regular expression-less solution. It doesn't handle arrays of objects. You see how it shorts out and says, "If it's an array, let JSON.stringify handle it"? That's fine for simple value types, but not for objects.

//                                        \/\/\/ OH NOES!!! \/\/\/
if (typeof obj_from_json !== "object" || Array.isArray(obj_from_json)){
    // not an object, stringify using native function
    
    return JSON.stringify(obj_from_json);
//         ^^^^^^^^^^^^^^ No! Don't do that! 
}

I edited his fiddle with a fail case. Here's the payload:

var obj = {
  name: "John Smith",
  favoriteObjects: [
    { b: "there's", c: "always", d: "something" },
    [1, 2, "spam", { f: "hello" }],
    { vowels: "are missing" },
  ],
  favoriteFruits: ["Apple", "Banana"],
};

And here's the whitespaced output:

{
  name:"John Smith",
  favoriteObjects:[
    {
      "b":"there's",    <<< Major
      "c":"always",     <<< fails
      "d":"something"   <<< here
    },
    [      1,2,"spam",      {        "f":"hello"     <<< and here.      }    ],
    {
      "vowels":"are missing" <<< again.
    }
  ],
  favoriteFruits:["Apple","Banana"] <<< that worked!
}

See? Though the array of strings (favoriteFruits) works, we've still got the quotes on the second level of serialization for objects in arrays (like favoriteObjects). Bad.


A simple solution

I spent waaay too much time trying to clean up that function before figuring out a slick trick to reduce the complexity of the problem considerably, which I think brings regex back into the mix.

Let's stringify with whitespace

In the vein of Mr. McGuire, I just want to say one word to you, just one word:

Whitespace.

Let's JSON.stringify with some whitespace. That makes the regex to remove the quotes from the keys MUCH easier.

Start your solution with this:

var cleaned = JSON.stringify(x, null, 2);

The call to stringify says "stringify x (the first param) without a fancy replacer function (indicated by the second param of null) with two (3rd param of 2) spaces of whitespace for each level of depth in the serialization." That 2 also buys us separate lines for each property.

{
  "name": "John Smith",
  "favoriteObjects": [
    {
      "b": "there's",
      "c": "always",
// etc...

Every key is now nicely at the start of a line. That we can handle.


NOW clean the quotes off of keys

Let's create a regex that looks for keys, and only keys, and pulls off their surrounding ". Since we know that keys are all on their own lines now, things are much simpler.

The only real gotcha is if the value to a property contains ": midway through. That can screw things up.

"a": [    "This could \": be bad",    "QQ"]
Lookbehind Caveat

I wanted to solve this with a fancy negative look-ahead for older browsers, but gave up and used a negative lookbehind. Negative lookbehind is only supported by a subset of browsers after 2018 (description of history of lookbehind proposal at 2ality here, browser compatibility can be found here).


RegEx Explained:
/^[\t ]*"[^:\n\r]+(?<!\\)":/gm

That regex...

  • Is a regex
    • Starts with /
  • Looks for any line that starts with (0-to-infinity spaces or tabs)
    • ^[\t ]*
  • Then a "
    • "
  • Then one or more characters of anything that ISN'T a : or a newline character
    • [^:\n\r]+
  • Then that ends with the two characters ": that (negative lookbehind warning) isn't preceded by a backslash
  • Does this globally and multi-line (line by line)
    • /gm

Fiddle of reasonable success.

Result:

{
  name: "John Smith",
  favoriteObjects: [
    {
      b: "there's",
      c: "always",
      d: "something"
    },
    [      1,      2,      "spam",      {        f: "hello"      }    ],
    {
      vowels: "are missing"
    }
  ],
  favoriteFruits: [
    "Apple",
    "Banana"
  ]
}

QED a short eight and a half years after jadent asked. Now that's service!


Quick Update: An even better answer, if you don't necessarily need this to happen in code (though in some use cases, you could call this library directly too), could be to use prettier, which cleans things beautifully.

Solution 4 - Javascript

You can look at the source code of json2.js a parser created by the one who defined the JSON format. Look for quote function calls: these surround a value by quotes. Keys are quoted at lines 326 and 338.

Do not include the library after the modification. Instead only take the relevant (stringify) part, or at least replace JSON with something else, eg. FAKEJSON.

For example, an object FAKEJSON which only defined stringify: http://jsfiddle.net/PYudw/

Solution 5 - Javascript

Use JSON5.stringify

JSON5 is a superset of JSON that allows ES5 syntax, including unquoted property keys. The JSON5 reference implementation (json5 npm package) provides a JSON5 object that has the same methods with the same args and semantics as the built-in JSON object.

It is highly likely that the service you are using is using this library.

Solution 6 - Javascript

Found a good NPM package to do just this:

https://www.npmjs.com/package/stringify-object

const stringify = require('stringify-object')

let prettyOutput = stringify(json);

Works pretty well.

Solution 7 - Javascript

Try to use the servive with JSONP, I guess they offer it when using this format.

Else, file them a detailed bug report including a good argumentation why the should conform to the standard. Any other solution than eliminating the source problem is no real solution.

A quick-n-dirty fix might be to pipe the string through a regex before parsing it:

var obj = JSON.parse(str.replace(/(\{|,)\s*(.+?)\s*:/g, '$1 "$2":'));

Or you try to adjust a existing javascript JSON parser (like this one) if you want a more syntactical parse.

Solution 8 - Javascript

Your inherited syntax should be easily eaten by YAML, which is a superset of JSON.

Try the JavaScript YAML parser and dumper: http://nodeca.github.com/js-yaml/

Solution 9 - Javascript

@Derek朕會功夫 Thanks for sharing this method, I'de like to share my code which supports stringifying an array of objects as well.

export const stringifyObjectWithNoQuotesOnKeys = (obj_from_json) => {
    // In case of an array we'll stringify all objects.
    if (Array.isArray(obj_from_json)) {
        return `[${
                    obj_from_json
                        .map(obj => `${stringifyObjectWithNoQuotesOnKeys(obj)}`)
                        .join(",")
                }]` ;
    }
    // not an object, stringify using native function
    if(typeof obj_from_json !== "object" || obj_from_json instanceof Date || obj_from_json === null){
        return JSON.stringify(obj_from_json);
    }
    // Implements recursive object serialization according to JSON spec
    // but without quotes around the keys.
    return `{${
            Object
                .keys(obj_from_json)
                .map(key => `${key}:${stringifyObjectWithNoQuotesOnKeys(obj_from_json[key])}`)
                .join(",")
            }}`;
};

Solution 10 - Javascript

CSVJSON's JSON Beautifier has an option to drop quotes on keys. If you want just the code, you can copy it from the GitHub repo. I modified Douglas Crockford's JSON2 to add support for that.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjadentView Question on Stackoverflow
Solution 1 - JavascriptDerek 朕會功夫View Answer on Stackoverflow
Solution 2 - JavascriptfinoView Answer on Stackoverflow
Solution 3 - JavascriptruffinView Answer on Stackoverflow
Solution 4 - JavascriptRob WView Answer on Stackoverflow
Solution 5 - JavascriptInigoView Answer on Stackoverflow
Solution 6 - Javascriptuser1543276View Answer on Stackoverflow
Solution 7 - JavascriptBergiView Answer on Stackoverflow
Solution 8 - Javascriptuser1649339View Answer on Stackoverflow
Solution 9 - JavascriptAdel BacheneView Answer on Stackoverflow
Solution 10 - JavascriptMartin DrapeauView Answer on Stackoverflow