用于解析单个键的正则表达式:Javascript 中 JSON 中的值
Posted
技术标签:
【中文标题】用于解析单个键的正则表达式:Javascript 中 JSON 中的值【英文标题】:Regex for parsing single key: values out of JSON in Javascript 【发布时间】:2012-02-03 17:27:36 【问题描述】:我正在尝试查看是否可以从 javascript 中的 JSON
字符串中查找单个 keys
并将其返回为 Value
和 Regex
。有点像构建一个JSON
搜索工具。
想象一下下面的 JSON
"
"Name": "Humpty",
"Age": "18",
"Siblings" : ["Dracula", "Snow White", "Merlin"],
"Posts": [
"Title": "How I fell",
"Comments": [
"User":"Fairy God Mother",
"Comment": "Ha, can't say I didn't see it coming"
]
]
"
我希望能够搜索JSON
字符串并且只提取单个属性。
让我们假设它已经是function
,它看起来像。
function getPropFromJSON(prop, JSONString)
// Obviously this regex will only match Keys that have
// String Values.
var exp = new RegExp("\""+prop+"\"\:[^\,\]*");
return JSONString.match(exp)[0].replace("\""+prop+"\":","");
它将为Key
返回Value
的子字符串。
例如
getPropFromJSON("Comments")
> "[
"User":"Fairy God Mother",
"Comment": "Ha, can't say I didn't see it coming"
]"
如果您想知道我为什么要这样做而不是使用JSON.parse()
,我正在围绕localStorage
构建一个JSON 文档存储。 localStorage
仅支持键/值对,因此我将整个 Document
的 JSON
字符串存储在唯一的 Key
中。我希望能够对文档运行查询,理想情况下没有 JSON.parsing()
的开销 Documents
的整个 Collection
然后递归 Keys
/nested Keys
以找到匹配项。
我在 regex
方面不是最擅长的,所以我不知道如何做到这一点,或者单独使用 regex
是否可行。这只是一个实验,以确定它是否可能。任何其他想法作为解决方案将不胜感激。
【问题讨论】:
这是 JSON,你为什么不直接把它当成 JSON? @fge 如果您阅读他的帖子,您就会明白原因。尽管这并不是使用正则表达式的真正正当理由。请记住正则表达式是一种常规语言.. 不应该用于此 我怀疑为此实现自己的解析是否值得。如果您只需要一个巨大文档中的单个值,它可能会执行得更好,但是如果您进行多次搜索,将 json 保存在内存中应该会更快。 @kapep 我本来打算实现cache
,但仅用于检索到的Documents
。我无法想象将所有文档存储在内存中会更快。我仍然需要在整个Collection
中的每个Document
中的Keys
上使用iterate
。这比JSON.parse()
对性能的影响要大得多。
尝试实现这样的东西是没有意义的。您可以使用状态机来解析 JSON,但尝试使用正则表达式永远无法完全满足您的需求,因为 JSON 值可以是数组、对象、字符串、数字、函数等。
【参考方案1】:
我强烈建议您不要这样做。 JSON 不是一种常规语言,此处明确说明:https://cstheory.stackexchange.com/questions/3987/is-json-a-regular-language
引用上面的帖子:
例如,考虑一个数组数组的数组:
[ [ [ 1, 2], [2, 3] ] , [ [ 3, 4], [ 4, 5] ] ]
显然你无法用真正的正则表达式解析它。
我建议将您的 JSON 转换为一个对象 (JSON.parse) 并实现一个查找函数来遍历该结构。
除此之外,您还可以看看 Douglas Crockford 的 json2.js parse 方法的精髓。也许更改后的版本将允许您搜索 JSON 字符串并仅返回您正在寻找的特定对象,而无需将整个结构转换为对象。仅当您从未从 JSON 中检索任何其他数据时,这才有用。如果你这样做了,你还不如从一开始就改变了整个事情。
编辑
为了进一步展示正则表达式是如何分解的,这里是一个尝试解析 JSON 的正则表达式
如果您将其插入http://regexpal.com/ 并选中“点匹配所有”。你会发现它可以很好地匹配一些元素,比如:
正则表达式
"Comments"[ :]+((?=\[)\[[^]]*\]|(?=\)\[^\]*\|\"[^"]*\")
JSON 匹配
"Comments": [ "User":"Fairy God Mother", "Comment": "Ha, can't say I didn't see it coming" ]
正则表达式
"Name"[ :]+((?=\[)\[[^]]*\]|(?=\)\[^\]*\|\"[^"]*\")
JSON 匹配
"Name": "Humpty"
但是,一旦您开始查询具有嵌套数组的“Posts”等高级结构,您就会发现无法正确返回该结构,因为正则表达式没有指定“]”的上下文结构的末端。
正则表达式
"Posts"[ :]+((?=\[)\[[^]]*\]|(?=\)\[^\]*\|\"[^"]*\")
JSON 匹配
"Posts": [ "Title": "How I fell", "Comments": [ "User":"Fairy God Mother", "Comment": "Ha, can't say I didn't see it coming" ]
【讨论】:
我之前看过json2.js的解析方法。它并没有真正进行任何类型的解析。它只是做了很多替换坏/危险/转义字符/内容/脚本的工作,因此 JSON 是干净的。然后它只是将干净的字符串传递给eval();
。我认为您有权单独使用Regex
。我将尝试使用JS
和Regex
的组合。对于我的用例,我不同意转换整个事物并遍历它。对大型 collections || documents
来说过于密集,更不用说在多个属性上进行搜索和匹配了。
很公平。我唯一可以推荐的另一件事(我不是该领域的专家)是使用关系数据友好的格式。我假设 Ms-Sql、mysql 和 Oracle 具有存储数据的最佳方式,因此读取、写入、比较和连接数据的速度非常快(我怀疑它是否存储为 JSON)。只是一个想法。
您应该遵循此答案中的建议,并避免通过正确反序列化 JSON 和搜索结果结构之外的任何方法执行此操作。
如果你对 JSON 的嵌套深度设置一个有限的固定限制,它就会成为一种常规语言,但是除非你的限制只有 1 或 2,否则正则表达式会非常难看。【参考方案2】:
\|\|\[|\]|,|:|(\\-)?\\d+(\\.\\d+)?|".+?"
您可以使用以下正则表达式并通过匹配来遍历 json 的所有标记。您可以对 JSON 进行标记,但解析部分必须由您自己实现。
由于您使用的是我从标签中假设的 JavaScript,因此对 JSON 进行编码的最佳方式仍然是 JSON.parse()
。
【讨论】:
【参考方案3】:我迟到了将近 10 年,但我想出了这个。 没有在比这更疯狂的 JSON 中进行测试,但它解决了我的用例。
const obj1 =
id: 1,
'name.1': '123',
address:
'address.1': 'Chicken Dinner Road, 69',
'address.2': 'Psycho lane, 666',
,
'age.1':
'thisIsSomeCrazyJson.3': 10,
age: 50,
,
types: [
id: 22,
'name.name': '123',
typeOption:
id: 1,
'whoTFWroteThisJSON.2': '123',
,
,
id: 32,
'name.1': '123',
,
],
;
const obj2 =
Name: 'Humpty',
Age: '18',
Siblings: ['Dracula', 'Snow White', 'Merlin'],
Posts: [
Title: 'How I fell',
Comments: [
'User': 'Fairy God Mother',
'Comment': "Ha, can't say I didn't see it coming",
,
],
,
],
;
function matchKeyDeep(input, pattern)
return Object.entries(input).reduce((nextInput, [key, value]) =>
const isMatch = pattern.test(key);
if (Array.isArray(value))
const arrValue = value;
let nextValue = arrValue.map((arrItem) =>
if (typeof arrItem === 'object')
return matchKeyDeep(arrItem, pattern);
return arrItem;
);
if (!isMatch && Array.isArray(nextValue))
nextValue = nextValue.filter((v) => (typeof v === 'object' && v !== null));
if (nextValue.length === 0) return nextInput;
nextInput[key] = nextValue;
return nextInput;
if (typeof value === 'object')
const recurse = matchKeyDeep(value, pattern);
if (!isMatch && Object.keys(recurse).length === 0)
return nextInput;
nextInput[key] = recurse;
return nextInput;
if (isMatch)
nextInput[key] = value;
return nextInput;
, );
const res = matchKeyDeep(obj1, /\.\d/);
const res2 = matchKeyDeep(obj2, /Comment/);
console.log(res);
console.log(res2);
【讨论】:
【参考方案4】:首先,对 JSON 对象进行字符串化。然后,您需要存储匹配子字符串的开头和长度。例如:
"matched".search("ch") // yields 3
对于 JSON 字符串,它的工作原理完全相同(除非您明确搜索逗号和大括号,在这种情况下,我建议您在执行正则表达式之前先对 JSON 对象进行一些转换(即认为:、、) .
接下来,您需要重构 JSON 对象。我编写的算法通过从匹配索引递归地向后检测 JSON 语法来做到这一点。例如,伪代码可能如下所示:
find the next key preceding the match index, call this theKey
then find the number of all occurrences of this key preceding theKey, call this theNumber
using the number of occurrences of all keys with same name as theKey up to position of theKey, traverse the object until keys named theKey has been discovered theNumber times
return this object called parentChain
有了这些信息,就可以使用正则表达式过滤一个 JSON 对象以返回键、值和父对象链。
你可以在http://json.spiritway.co/看到我编写的库和代码
【讨论】:
以上是关于用于解析单个键的正则表达式:Javascript 中 JSON 中的值的主要内容,如果未能解决你的问题,请参考以下文章