JSON.stringify 在属性上没有引号?

IT技术 javascript json
2021-01-31 23:04:21

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

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

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

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

6个回答

在大多数情况下,这个简单的正则表达式解决方案适用于取消引用 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/

@Derek 朕会功夫你的正则表达式/\"([^(\")"]+)\":/g可以简化为/"([^"]+)":/g,参见regex101.com/r/qnz0ld/2
2021-03-21 23:04:21
-1 也不是我,但你必须仔细阅读问题。OP 需要将对象编码为(损坏的)json,而不是解析/评估它。
2021-03-22 23:04:21
@Derek 这种方法不可靠例如,输入以下内容:{"foo":"e\":bar"}(valid JSON) 变为{foo:e:bar"}(...)!
2021-04-05 23:04:21
@endriu 在这种情况下,只需为空值添加额外的检查。
2021-04-11 23:04:21
@Derek/\\\"/可以简化为/\\"/. 不要忘记添加全局标志/\\"/g,否则它会在带有多个\". 至于随机字符,千万不要使用文字 U+FFFF,以防编辑器窒息,而是使用转义序列。用于恢复的正则表达式将变为/\uFFFF/g.
2021-04-12 23:04:21

看起来这是您正在寻找的一个简单的 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

根据您的服务器的语言,您需要对象到字符串或检查。

util.inspect() 在将对象写入 Neo4j 查询时对我来说非常棒,可以一次设置多个参数。
2021-03-15 23:04:21
好的,那么您如何在客户端 javascript 中执行此操作?你能举个例子吗?没有它,这并不能真正回答问题。
2021-03-15 23:04:21
从 nodejs 链接: > util.inspect() 方法返回用于调试的对象的字符串表示。util.inspect 的输出可能随时更改,不应以编程方式依赖。
2021-03-28 23:04:21
非常感谢你!这正是我正在寻找的。我使用 json 通过 ws 服务器向我的游戏发送数据,不管你信不信,不必处理属性名称周围的额外引号可以节省大量数据!只是为了澄清,.toSource()在 nodejs 中也可以正常工作,但不适用于数组中的对象。util检查在数组这是美妙的数组和对象的作品,喜欢它。
2021-03-31 23:04:21

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 或奇怪的是,截至 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用空格

McGuire 先生脉络中,我只想对您说一句话,就一句话:

空白。

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

用这个开始你的解决方案:

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

调用stringify说“字符串化x(第一个参数),没有花哨的替换函数(由 的第二个参数表示null2,对于序列化中的每个深度级别,都有两个(第三个参数)空格的空格。” 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 ]*
  • 然后一个 "
    • "
  • 随后的一个或多个字符什么IS NOT一个:或一个换行符
    • [^:\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"
  ]
}

在贾登特提出要求后的短短八年半时间里,QED 提出了要求。现在就是服务!


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

这个解决方案为我做了:我做了“remove (?<!\)”一个用过的 \s 表示空格和制表符 jsonStr.replace(/^\s*"[^:\n\r]+":/gm , function (match) { return match.replace(/"/g, ''); })
2021-03-15 23:04:21
为我工作,很好的解释。谢谢
2021-04-02 23:04:21

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

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

例如,FAKEJSON只定义了一个对象stringifyhttp : //jsfiddle.net/PYudw/

这是一个好主意。我会分叉 repo,删除引号,并将 JSON 对象重命名为类似 FAILJSON 之类的滑稽内容,以表明您没有使用实际的 JSON 对象或实际的 JSON。
2021-03-14 23:04:21
@Derek 不应该包含整个库。只取JSON.stringify一部分,去掉引号。由于库是由定义 JSON 的人创建的,我们可以非常确定结果是非常有效的 JSON。
2021-03-20 23:04:21
是的,同意德里克的观点。虽然他的替代者工作得很好,但我对克劳福德的代码更有信心,没有对德里克的不尊重,哈哈。 .toSource()效果很好,但不包括您的对象是否在数组中,这很糟糕(我在节点上,因此浏览器兼容性不是问题:P)所以我将使用此方法也感谢@RobW,jsfiddle 链接似乎卡在加载页面:(
2021-03-28 23:04:21
看起来这是最好的方法。
2021-04-01 23:04:21
当您可以在纯 JavaScript 中完成时,为什么还需要一个额外的库?
2021-04-11 23:04:21

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

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

const stringify = require('stringify-object')

let prettyOutput = stringify(json);

效果很好。

这个库不是递归的。
2021-03-15 23:04:21