117

我正在使用使用不正确 JSON 格式的服务(属性周围没有双引号)。所以我需要发送

{ name: "John Smith" }代替{ "name": "John Smith" }

此格式无法更改,因为这不是我的服务。

任何人都知道字符串化路由来格式化上面的 JavaScript 对象吗?

4

10 回答 10

145

在大多数情况下,这个简单的正则表达式解决方案可以取消引用 JSON 属性名称:

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"}

极端情况:

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

特别感谢 Rob W 修复它。

限制

在正常情况下,上述正则表达式会起作用,但从数学上讲,用正则表达式描述 JSON 格式是不可能的,这样它就可以在每种情况下都起作用(用正则表达式计算相同数量的花括号是不可能的。)因此,我有创建一个新函数以通过原生函数正式解析 JSON 字符串并重新序列化它来删除引号:

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}}`;
}

示例:https ://jsfiddle.net/DerekL/mssybp3k/

于 2012-06-27T19:22:32.880 回答
25

看起来这是您正在寻找的一个简单的 Object toString 方法。

在 Node.js 中,这是通过使用 util 对象并调用 util.inspect(yourObject) 来解决的。这会给你你想要的一切。点击此链接以获取更多选项,包括方法应用的深度。http://nodejs.org/api/util.html#util_util_inspect_object_options

因此,您要寻找的基本上是对象检查器,而不是 JSON 转换器。JSON 格式指定所有属性必须用双引号引起来。因此,不会有 JSON 转换器来执行您想要的操作,因为这根本不是 JSON 格式。此处的规格: https ://developer.mozilla.org/en-US/docs/Using_native_JSON

对象到字符串或检查是您需要的,具体取决于服务器的语言。

于 2013-11-01T23:58:10.767 回答
13

TL;DR——代码

下面有一个关于浏览器中的后向支持的警告,在这里你可以:

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

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

注意: IE 不支持 Lookbehinds,奇怪的是,截至 2021 年 6 月 18 日的任何版本的 Safari都不支持 Lookbehinds 。要在那里使用,(?<!\\)请从正则表达式中删除并注意下面提到的“陷阱”条件。

它以漂亮的方式进行字符串化,使用我们的正则表达式查找键,然后通过替换函数替换每个键匹配,该替换函数显示“这是您的匹配项,已删除所有双引号”。由于我们只将键匹配到它们的冒号,这正是我们想要的。

下面详细说明。


为什么接受的答案不正确

不幸的是,正如Adel 指出的那样, Derek 的无正则表达式解决方案存在一个非常严重的问题。它不处理对象数组。您看到它是如何短路并说“如果它是一个数组,让我们JSON.stringify处理它”?这对于简单的值类型很好,但不适用于对象。

//                                        \/\/\/ 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! 
}

我用一个失败的案例编辑了他的小提琴。这是有效载荷:

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

这是空白输出:

{
  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!
}

看?尽管字符串favoriteFruits数组favoriteObjects(坏的。


一个简单的解决方案

在想出一个巧妙的技巧来显着降低问题的复杂性之前,我花了太多时间来尝试清理该函数,我认为这会将正则表达式重新融入其中。

让我们stringify用空格

本着麦奎尔先生的精神,我只想对你说一句话,就一个字:

空白。

让我们JSON.stringify用一些空格。这使得正则表达式从键中删除引号更容易。

以此开始您的解决方案:

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

调用stringify说“字符串化x(第一个参数)没有花哨的替换函数(由 的第二个参数表示null),序列化中的每个深度级别都有两个(第三个参数2)空格。” 这2也为我们购买了每个属性的单独行。

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

现在,每个键都很好地位于一行的开头。我们可以处理的。


现在清除键的引号

让我们创建一个正则表达式来查找键,并且只查找键,然后将其周围的". 由于我们知道现在所有键都在自己的线路上,所以事情要简单得多

唯一真正的问题是属性的值是否包含":中途。那可能会把事情搞砸。

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

后视警告

我想通过对旧版浏览器的花哨的负前瞻来解决这个问题,但放弃了并使用了负后瞻。Negative lookbehind 仅在 2018 年之后被部分浏览器支持(此处描述了2ality 的lookbehind 提案的历史,浏览器兼容性可在此处找到)。


正则表达式解释:

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

那个正则表达式...

  • 是一个正则表达式
    • 以。。开始/
  • 查找以(0 到无穷大的空格或制表符)开头的任何行
    • ^[\t ]*
  • 然后一个"
    • "
  • 然后是一个或多个不是ISN: 's 或换行符的字符
    • [^:\n\r]+
  • 然后以两个字符结尾":负后向警告前面没有反斜杠
  • 全局和多行(逐行)
    • /gm

合理成功的小提琴

结果:

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

在jadent 问之后短短八年半的时间里,QED。现在是服务!


快速更新:如果你不一定需要在代码中发生这种情况(尽管在某些用例中,你也可以直接调用这个库),一个更好的答案可能是使用prettier,它可以很好地清理东西。

于 2020-12-24T21:02:15.697 回答
6

您可以查看由定义 JSON 格式的人创建的解析器的源代码。寻找函数调用:这些用引号包围一个值。密钥在第326338行引用。json2.js quote

修改后不要包含库。取而代之的是只取相关的 ( stringify) 部分,或者至少JSON用其他东西替换,例如。FAKEJSON.

例如,FAKEJSON仅定义的对象:http stringify: //jsfiddle.net/PYudw/

于 2012-06-27T19:31:15.437 回答
5

找到了一个很好的 NPM 包来做到这一点:

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

const stringify = require('stringify-object')

let prettyOutput = stringify(json);

效果很好。

于 2018-12-31T17:37:57.660 回答
5

利用JSON5.stringify

JSON5是 JSON 的超集,它允许 ES5 语法,包括不带引号的属性键。JSON5 参考实现(json5npm 包)提供了一个JSON5对象,该对象具有与内置JSON对象相同的方法和相同的参数和语义。

您正在使用的服务很可能正在使用此库。

于 2020-05-04T19:09:10.470 回答
3

尝试将服务与 JSONP 一起使用,我猜他们在使用这种格式时会提供它。

否则,向他们提交一份详细的错误报告,包括一个很好的论证为什么应该符合标准。除了消除源问题之外的任何其他解决方案都不是真正的解决方案。

一个快速n-dirty修复可能是在解析字符串之前通过正则表达式传递字符串:

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

或者,如果您想要更语法的解析,您可以尝试调整现有的 javascript JSON 解析器(例如这个)。

于 2012-06-27T19:33:18.177 回答
2

你继承的语法应该很容易被 YAML 吃掉,它是 JSON 的超集。

试试 JavaScript YAML 解析器和转储器:http: //nodeca.github.com/js-yaml/

于 2013-08-26T00:58:06.483 回答
2

@Derek朕会功夫感谢分享这个方法,我想分享我的代码,它也支持对对象数组进行字符串化。

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(",")
            }}`;
};
于 2019-04-08T18:43:15.483 回答
1

CSVJSON 的 JSON Beautifier有一个选项可以在键上去掉引号。如果您只需要代码,可以从 GitHub 存储库复制它。我修改了 Douglas Crockford 的 JSON2来增加对它的支持。

于 2016-10-06T22:39:53.870 回答