如何将不可预测的JSON解析为字符串?
假设我有一个node.js应用程序,它以一种奇怪的格式接收输入:带有JSON的字符串随意洒入其中,如下所示:
This is a string {"with":"json","in":"it"} followed by more text {"and":{"some":["more","json"]}} and more text
我对这个输入文本有几个保证:
- JSON对象之间的文字文本位总是没有花括号。
- 推入文本的顶级JSON对象始终是对象文字,而不是数组。
我的目标是将其拆分为一个数组,单独保留文字文本并解析出JSON,如下所示:
[
"This is a string ",
{"with":"json","in":"it"},
" followed by more text ",
{"and":{"some":["more","json"]}},
" and more text"
]
到目前为止,我已经编写了一个naive solution,它只是计算花括号来决定JSON的起点和终点。但是,如果JSON包含带有花括号的字符串{"like":"this one } right here"}
,则无法使用。我可以尝试通过类似的引用计数数学来解决这个问题,但是我还必须考虑转义的报价。在那一刻,我觉得我正在重做太多JSON.parse
的工作。有没有更好的方法来解决这个问题?
这是一个相对简单的蛮力方法:将整个输入字符串拆分为花括号,然后按顺序逐步遍历数组。每当遇到一个开放式大括号时,从成功解析为JSON的起点找到数组中最长的一块。冲洗并重复。
如果输入包含无效的JSON和/或不平衡的大括号,则无效(请参阅下面的最后两个测试用例。)
const tryJSON = input => {
try {
return JSON.parse(input);
} catch (e) {
return false;
}
}
const parse = input => {
let output = [];
let chunks = input.split(/([{}])/);
for (let i = 0; i < chunks.length; i++) {
if (chunks[i] === '{') {
// found some possible JSON; start at the last } and backtrack until it works.
for (let j = chunks.lastIndexOf('}'); j > i; j--) {
if (chunks[j] === '}') {
// Does it blend?
let parsed = tryJSON(chunks.slice(i, j + 1).join(""))
if (parsed) {
// it does! Grab the whole thing and skip ahead
output.push(parsed);
i = j;
}
}
}
} else if (chunks[i]) {
// neither JSON nor empty
output.push(chunks[i])
}
}
console.log(output)
return output
}
parse(`{"foo": "bar"}`)
parse(`test{"foo": "b}ar{{[[[{}}}}{}{}}"}`)
parse(`this {"is": "a st}ri{ng"} with {"json": ["in", "i{t"]}`)
parse(`{}`)
parse(`this {"i{s": invalid}`)
parse(`So is {this: "one"}`)
如何将不可预测的JSON解析为字符串?
假设我有一个node.js应用程序,它以一种奇怪的格式接收输入:带有JSON的字符串随意洒入其中,如下所示:
This is a string {"with":"json","in":"it"} followed by more text {"and":{"some":["more","json"]}} and more text
我对这个输入文本有几个保证:
- JSON对象之间的文字文本位总是没有花括号。
- 推入文本的顶级JSON对象始终是对象文字,而不是数组。
我的目标是将其拆分为一个数组,单独保留文字文本并解析出JSON,如下所示:
[
"This is a string ",
{"with":"json","in":"it"},
" followed by more text ",
{"and":{"some":["more","json"]}},
" and more text"
]
到目前为止,我已经编写了一个naive solution,它只是计算花括号来决定JSON的起点和终点。但是,如果JSON包含带有花括号的字符串{"like":"this one } right here"}
,则无法使用。我可以尝试通过类似的引用计数数学来解决这个问题,但是我还必须考虑转义的报价。在那一刻,我觉得我正在重做太多JSON.parse
的工作。有没有更好的方法来解决这个问题?
这是一个相对简单的蛮力方法:将整个输入字符串拆分为花括号,然后按顺序逐步遍历数组。每当遇到一个开放式大括号时,从成功解析为JSON的起点找到数组中最长的一块。冲洗并重复。
如果输入包含无效的JSON和/或不平衡的大括号,则无效(请参阅下面的最后两个测试用例。)
const tryJSON = input => {
try {
return JSON.parse(input);
} catch (e) {
return false;
}
}
const parse = input => {
let output = [];
let chunks = input.split(/([{}])/);
for (let i = 0; i < chunks.length; i++) {
if (chunks[i] === '{') {
// found some possible JSON; start at the last } and backtrack until it works.
for (let j = chunks.lastIndexOf('}'); j > i; j--) {
if (chunks[j] === '}') {
// Does it blend?
let parsed = tryJSON(chunks.slice(i, j + 1).join(""))
if (parsed) {
// it does! Grab the whole thing and skip ahead
output.push(parsed);
i = j;
}
}
}
} else if (chunks[i]) {
// neither JSON nor empty
output.push(chunks[i])
}
}
console.log(output)
return output
}
parse(`{"foo": "bar"}`)
parse(`test{"foo": "b}ar{{[[[{}}}}{}{}}"}`)
parse(`this {"is": "a st}ri{ng"} with {"json": ["in", "i{t"]}`)
parse(`{}`)
parse(`this {"i{s": invalid}`)
parse(`So is {this: "one"}`)