【问题标题】:Trying to implement a JSON parsing function in JavaScript to output a string in a prettier format [duplicate]尝试在 JavaScript 中实现 JSON 解析函数以更漂亮的格式输出字符串 [重复]
【发布时间】:2021-04-09 22:11:00
【问题描述】:

我很清楚 npm 包中存在 JSON.parseJSON.stringifyprettier。但由于某种原因,我仍然必须手动执行此操作。把它想象成一个编码面试问题,请不要仅仅因为我可以使用JSON.parseJSON.stringify 而关闭这个问题,正如我所说的那样有这个限制。

这是一个字符串"['foo', {bar:['baz',null,1.0,2]}]"

我想实现一个函数来返回一个字符串,该字符串表示具有适当缩进的 json 对象。

即输出 string 应该是

   [
        "foo", 
        {
            "bar":
            [
                "baz", 
                null, 
                1.0, 
                2
            ]
        }
    ]

这是我的尝试

function printJSON(str) {
    let spaces = [];
    let output = '';

    str.split('').forEach(char => {
        switch(char) {
            case '{':
            case '[':
                spaces.push('    ');
                output += char + '\n' + spaces.join('');
                break;
            case '}':
            case ']':
                spaces.pop();
                output += '\n' + spaces.join('') + char;
                break;
            case ',':
                output += char + '\n' + spaces.join('');
                break;
            default:
                output += char;
                break;
        }
    });

    console.log(output);
    return output
}

但输出格式与中略有不同

[ 
    "foo", 
     { 
        bar:[ // ????
            "baz", 
            null, 
            1.0, 
            2 
        ] 
    } 
] 

如何解决此格式问题?还有没有更优雅的方式或替代方式来实现这一点?

【问题讨论】:

  • 你打算自己推出这个功能吗?有些包(例如,prettier)已经很好地做到了这一点
  • Joji,我在 luawtf ​​的答案中添加了一个部分,向您展示如何自己完成此操作。
  • @Thankyou 感谢您更新答案!新年快乐。你能在这里解释一下t?.constructor吗?
  • t.constructor 允许我们检查任何t 的类型。然而,因为我们希望允许t 成为nullundefined,所以我们使用空值合并?. 来防止对空值进行属性查找。您可以在我的一些other answers 中看到这种技术。
  • @谢谢你的更新!但是,当输入为字符串时,它似乎不起作用。例如如果输入是"['foo', {bar:['baz',null,1.1,2]}]",则输出将是一个未格式化的字符串。

标签: javascript json


【解决方案1】:

你试过JSON.stringify的内置漂亮打印吗?

例如:

console.log(JSON.stringify(
  ['foo', { bar: ['baz', null, 1.0, 2 ] } ],
  null,
  4 // Indent, specify number of spaces or string
));

输出:

[
    "foo",
    {
        "bar": [
            "baz",
            null,
            1,
            2
        ]
    }
]

如果您想将字符串用作美化代码的函数的输入:

function pretty(string) {
  var object = JSON.parse(string) /* or eval(string) */;
  return JSON.stringify(object, null, 4);
}

不过,推出自己的解决方案并不困难。我们从写作开始-

const input =
  ['foo', {bar:['baz',null,1.1,2]}]

const result =
  toJson(input)

toJson 对输入 t 进行简单类型分析 -

function toJson (t, i = "  ", d = 0)
{ function toString(t)
  { switch (t?.constructor)
    { case Object:
        return [ "{", indent(Object.entries(t).map(([k, v]) => `${k}:\n${toString(v)}`).join(",\n"), i, d + 1), "}" ].join("\n")
      case Array:
        return [ "[", indent(t.map(v => toString(v)).join(",\n"), i, d + 1), "]" ].join("\n")
      case String:
        return `"${t}"`
      case Boolean:
        return t ? "true" : "false"
      default:
        return String(t ?? null)
    }
  }
  return indent(toString(t), i, d)
}

indent 在哪里-

const indent = (s, i = "  ", d = 0) =>
  s.split("\n").map(v => i.repeat(d) + v).join("\n")

展开下面的sn-p,在你自己的浏览器中运行toJson——

function toJson (t, i = "  ", d = 0)
{ function toString(t)
  { switch (t?.constructor)
    { case Object:
        return [ "{", indent(Object.entries(t).map(([k, v]) => `${k}:\n${toString(v)}`).join(",\n"), i, d + 1), "}" ].join("\n")
      case Array:
        return [ "[", indent(t.map(v => toString(v)).join(",\n"), i, d + 1), "]" ].join("\n")
      case String:
        return `"${t}"`
      case Boolean:
        return t ? "true" : "false"
      default:
        return String(t ?? null)
    }
  }
  return indent(toString(t), i, d)
}

const indent = (s, i = "  ", d = 0) =>
  s.split("\n").map(v => i.repeat(d) + v).join("\n")

const input =
  ['foo', {bar:['baz',null,1.1,2]}]

const result =
  toJson(input)

console.log(result)

结果符合要求-

[
  "foo",
  {
    bar:
    [
      "baz",
      null,
      1.1,
      2
    ]
  },
  true
]

您可以通过指定第二个参数来自定义缩进,例如制表符 -

const result =
  toJson(input, "\t") // indent using a tab

或者使用自定义数量的空格 -

const result =
  toJson(input, "    ") // indent using a four (4) spaces

【讨论】:

  • 除了 OP 以字符串而不是对象开头
  • 使用字符串代替对象很容易(通过JSON.parseeval),但既然你提到了它,我已经编辑了答案并添加了一个示例函数。
  • eval 是的,但是 OP 的字符串有单引号,使用 JSON.parse() 会失败。在答案编辑器中单击<> 可以在页面中创建一个可运行的堆栈sn-p
  • 这就是为什么我将eval 作为一个选项。但是,eval === evil 不应该使用,尤其是在输入未知的情况下,但我认为包含能够处理不带引号的对象属性的 JSON 解析函数不会有帮助。如果有人正在寻找支持不带引号的对象属性的 JSON 解析器,请查看 JSON5
  • 感谢您更新答案!我试图理解这一点。您能在这里解释一下t?.constructor 的用法吗?也许添加一些关于你如何构建这个答案的cmets?我实际上有点难以追随?。谢谢!
猜你喜欢
  • 2012-07-31
  • 2017-05-05
  • 2014-08-24
  • 2020-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多