145

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?

tanguy_k
  • 11,307
  • 6
  • 54
  • 58
jadent
  • 3,674
  • 5
  • 27
  • 32

16 Answers16

166

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/

Dan Dascalescu
  • 143,271
  • 52
  • 317
  • 404
Derek 朕會功夫
  • 92,235
  • 44
  • 185
  • 247
  • The first one was fine, wasn't it? `var x = eval(str);` – Rudie Jun 27 '12 at 19:24
  • Where does the -1 come from..? How is this not a good answer? – Rudie Jun 27 '12 at 19:25
  • -1 wasn't me, I think this may actually be the only way to do this. But it's bad practice to use eval (it will automatically fail jsLint). It's probably especially bad practice when the string being eval'd comes from a dodgy external source. – RichardTowers Jun 27 '12 at 19:27
  • 4
    -1 wasn't me either, but you must read the question carefully. OP needs to encode an object to (broken) json, not parse/evaluate it. – Salman A Jun 27 '12 at 19:28
  • 9
    @Derek This method is **not reliable**. For example, take this input: `{"foo":"e\":bar"}` (valid JSON) becomes `{foo:e:bar"}` (...)! – Rob W Jun 27 '12 at 19:40
  • @RobW - I have thought of that too. To fix it, simply add `no \" in between the quotes` to the regex – Derek 朕會功夫 Jun 27 '12 at 19:42
  • 1
    @Derek `/\\\"/` can be simplified to `/\\"/`. Don't forget to add the global flag, `/\\"/g`, or it will break on strings with multiple `\"`. As for the random character, **never** use a literal U+FFFF, in case the editor chokes, but an escape sequence. The regex for reverting would become `/\uFFFF/g`. – Rob W Jun 27 '12 at 19:59
  • Derek this is awesome!!! Because now i don't need to send the quotes over the websocket pipe. Can save a ton of bandwidth for my game, thank u sir! – NiCk Newman Jul 28 '15 at 14:34
  • Note that the + should be replaced by a * if you want to remove quotes of empty strings "" – leszek.hanusz Jan 10 '17 at 07:48
  • This will also remove the quotes on a parameters with a dot (e.g. "adr.city"), so this is the full, correct function IMHO: .replace(/\\"/g,"\uFFFF").replace(/\"([^(\"\.)"]+)\":/g,"$1:").replace( /,(\S)/g, ", $1").replace(/\uFFFF/g,"\\\"") (note that I've also added a space after commas for readability - remove it if you need "strict" truth ;) – Nico Feb 14 '18 at 18:18
  • 2
    @Derek朕會功夫 your regex `/\"([^(\")"]+)\":/g` can be simplified to `/"([^"]+)":/g`, see https://regex101.com/r/qnz0ld/2 – tanguy_k Feb 27 '18 at 18:42
  • @Derek朕會功夫 it fails if the value is null. – asliwinski Jul 12 '18 at 12:54
  • 1
    @endriu In that case just add an additional check for null values. – Derek 朕會功夫 Jul 12 '18 at 13:00
  • 1
    It is possible to describe the whole JSON grammar as a regex, see [this answer](https://stackoverflow.com/a/3845829/2064544) – Tobias Mühl Mar 27 '19 at 04:40
  • 1
    @tanguy_k: you know you can edit answers, right? Anyway, I've done that for you. Amazing how that unnecessarily ugly regexp was left untouched since 2012 until I bothered to clean it up. – Dan Dascalescu Mar 07 '20 at 15:06
32

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.

fino
  • 3,764
  • 2
  • 23
  • 16
  • 1
    Thank u so much! This is exactly what I was looking for. I use json to emit data across the ws server to my game and believe it or not, not having to deal with the extra quotation marks around the property names saves an immense amount of data! Just to clarify, `.toSource()` works fine within nodejs as well, but doesn't work objects in arrays. The `util` inspect works for arrays and objects in arrays which is wonderful, love it. – NiCk Newman Jul 28 '15 at 18:02
  • 3
    `util.inspect()` just worked awesome for me while writing an object into a Neo4j query, to set multiple params at once. – agm1984 Aug 17 '17 at 03:29
  • 2
    From the nodejs link: > The util.inspect() method returns a string representation of object that is intended for debugging. The output of util.inspect may change at any time and should not be depended upon programmatically. – Peter Roehlen May 02 '18 at 15:26
  • 3
    okay so how do you do this in client-side javascript? Can you show an example? This doesn't really answer the question without it. – Daniel Kaplan Dec 30 '20 at 06:37
18

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.

ruffin
  • 16,507
  • 9
  • 88
  • 138
  • Worked for me, nice explanation. thanks – Voltan Sep 30 '21 at 03:11
  • this solution did it for me: I did the " remove (?<!\\) " an used \s for space and tab jsonStr.replace(/^\s*"[^:\n\r]+":/gm, function (match) { return match.replace(/"/g, ''); }) – jo_ Sep 30 '21 at 14:12
10

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. In fact, JSON5 is used by many high profile projects:

JSON5 was started in 2012, and as of 2022, now gets >65M downloads/week, ranks in the top 0.1% of the most depended-upon packages on npm, and has been adopted by major projects like Chromium, Next.js, Babel, Retool, WebStorm, and more. It's also natively supported on Apple platforms like MacOS and iOS.

~ json5.org homepage

Inigo
  • 12,186
  • 5
  • 41
  • 70
6

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/

Rob W
  • 341,306
  • 83
  • 791
  • 678
  • Why would you need an extra library when you can do it in pure JavaScript? – Derek 朕會功夫 Jun 27 '12 at 19:35
  • This is a good idea. I would fork the repo, remove the quotes, and rename the JSON object to something facetious like FAILJSON to make it clear that you are _not_ working with the actual JSON object, or actual JSON. – RichardTowers Jun 27 '12 at 19:37
  • @Derek The library should not be included as whole. Only take the `JSON.stringify` part, and remove the quotes. Since the library is **created by the one who defined JSON**, we can be pretty sure that the result is very valid JSON. – Rob W Jun 27 '12 at 19:39
  • Looks like this is the best approach. – Derek 朕會功夫 Jun 27 '12 at 20:13
  • Yeah, agree with Derek. Although his replacer works fine, I feel more confident with crawford's code, no disrespect to derek lol. `.toSource()` works good but doesn't include if your object is in array which is a bummer (and I'm on node so browser compat is not an issue :P) so i'll use this method thanks @RobW also, the jsfiddle link seems to be stuck at the loading page :( – NiCk Newman Jul 29 '15 at 08:33
6

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.

Inigo
  • 12,186
  • 5
  • 41
  • 70
user1543276
  • 436
  • 5
  • 11
5

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.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
2

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/

2

@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(",")
            }}`;
};
Adel Bachene
  • 974
  • 1
  • 14
  • 34
  • 1
    You should use `JSON.stringify` for instanceof Date too. Also return `'null'` if `obj_from_json` is null. – Far Dmitry May 08 '19 at 09:53
  • 1
    I just fixed the points raised by @FarDmitry by changing the second if condition to look like this: `if(typeof obj_from_json !== "object" || obj_from_json instanceof Date || obj_from_json === null)` – Brunno Vodola Martins Aug 28 '19 at 22:43
1

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.

Martin Drapeau
  • 1,484
  • 15
  • 16
0

If you want your JSON to be exported in such a way that it can be used directly inside JavaScript code, then the below solution should be what you're after. This solution works for any JavaScript variable and works fine with objects/arrays containing objects/arrays.

The best use case for the function below would be when you have JSON you'd like to include in JavaScript, without the unnecessary quotes. This can save you a lot of data, especially when you have short keys like "x" and "y".

function toUnquotedJSON(param){ // Implemented by Frostbolt Games 2022
    if(Array.isArray(param)){ // In case of an array, recursively call our function on each element.
        let results = [];
        for(let elem of param){
            results.push(toUnquotedJSON(elem));
        }
        return "[" + results.join(",") + "]";
    }
    else if(typeof param === "object"){ // In case of an object, loop over its keys and only add quotes around keys that aren't valid JavaScript variable names. Recursively call our function on each value.
        let props = Object
            .keys(param)
            .map(function(key){
                // A valid JavaScript variable name starts with a dollar sign (?), underscore (_) or letter (a-zA-Z), followed by zero or more dollar signs, underscores or alphanumeric (a-zA-Z\d) characters.
                if(key.match(/^[a-zA-Z_$][a-zA-Z\d_$]*$/) === null) // If the key isn't a valid JavaScript variable name, we need to add quotes.
                    return `"${key}":${toUnquotedJSON(param[key])}`;
                else
                    return `${key}:${toUnquotedJSON(param[key])}`;
            })
            .join(",");
        return `{${props}}`;
    }
    else{ // For every other value, simply use the native JSON.stringify() function.
        return JSON.stringify(param);
    }
}
Dysprosium
  • 344
  • 2
  • 9
0

My solution uses the Function constructor to create a temporary function which parses the string to JSON and then stringifies it:

function stringify2(notQuiteJsonString) {
    var tempFunc = new Function(`return JSON.stringify(${notQuiteJsonString})`)
    return tempFunc()
}

var jObj = `{a: 123, b: "This is b", c: {d: 10, c: "More stuff"}}`; 
console.log(stringify2(jObj));
// {"a":123,"b":"This is b","c":{"d":10,"c":"More stuff"}}
Randhir Rawatlal
  • 365
  • 1
  • 3
  • 13
0

Tried with bash/shell and nodejs command, and seems to work.

node <<END
> aaa = {
>   "abc": "xyz",
>   "num1": 1,
>   "text1": "my text",
>   "final": "the \"final\" text"
> }
> console.log(aaa)
> END
{ abc: 'xyz', num1: 1, text1: 'my text', final: 'the "final" text' }
DTY
  • 1
0

If the purpose of unquoting json is saving data to mysql then you can use CAST(JSON_UNQUOTE(?) AS JSON))

Eugene
  • 991
  • 10
  • 10
0

The reliable and simple solution

The accepted answer is not reliable, as already pointed out by other people.

There's a really simple approach which leverages JSON.stringify and RegEx. It uses RegEx to remove quotes from property names, but ensures that the regex won't affect values by temporarily replacing quotes in values. Simple, easy to understand, and reliable.

I use it to generate data for GraphQL queries.

function objectToGraphData(obj)
{
    // Make sure quotes within values are not affected by temporarily replacing them

    var quotePlaceholder = "#!-QUOT-" + Math.random() + "-QUOT-!#";

    var json = JSON.stringify(obj, function(key, val)
    {
        if (typeof(val) === "string")
        {
            return val.replace(/"/g, quotePlaceholder);
        }

        return val;
    });

    // Remove quotes surrounding property names and restore quotes in values

    json = json.replace(/"([^"]+?)":/g, "$1:");
    json = json.replace(new RegExp(quotePlaceholder, "g"), "\\\"");

    return json;
}

var test = [
  "testing 1-2-3",
  new Date(),
  {
    name: "Jamie Oliver",
    favoriteQuote: '"Mario": Let\'s go!',
    family: [
      {
        name: "James bond",
        favoriteQuote: '"Pedro": Of course we can!'
      }
    ]
  }
]

// Properties are not quoted:
console.log(objectToGraphData(test));
Jimmy Thomsen
  • 471
  • 3
  • 9
-1

as simple as

.replace(/[\[\]']+/g, '') 

this will replace:

[] with nothing

or

.replace(/[\{\}']+/g, '').

this will replace:

{} with nothing

Ali Seivani
  • 496
  • 3
  • 21