【问题标题】:Regex to match nested json objects正则表达式匹配嵌套的 json 对象
【发布时间】:2013-07-22 11:28:49
【问题描述】:

我正在实现某种解析器,我需要定位和反序列化 嵌入到其他半结构化数据中的 json 对象。我使用了正则表达式:

\\{\\s*title.*?\\}

定位对象

{title:'Title'}

但它不适用于嵌套对象,因为表达式只匹配第一个找到的右花括号。对于

{title:'Title',{data:'Data'}}

匹配

{title:'Title',{data:'Data'}

所以字符串对于反序列化变得无效。 我知道有一个贪婪的业务正在考虑,但我不熟悉正则表达式。您能否帮我扩展表达式以使用所有可用的右大括号。

更新:

明确地说,这是从半结构化数据(如嵌入了 JSON 的 HTML+JS)中提取 JSON 数据的尝试。我正在使用 GSon JAVA lib 来实际解析提取的 JSON。

【问题讨论】:

  • 当心“OMG,不要使用正则表达式,它是邪恶的!!”
  • .. 但说真的 - 为什么?手头的数据是什么,你需要用它来实现什么
  • @ViktorStolbin 有预制的 JSON 解析库。此外,由于 JSON 不是正则语言,因此无法用正则表达式正确解析(就像 HTML 一样)。
  • 你真的不能用正则表达式轻松做到这一点。 JSON 解析器的例子很多。如果可能的话,只要拿起一个,你会好很多。
  • @ViktorStolbin:我知道您并没有重新发明 JSON 库。我在这里想说的是,这是一个由两部分组成的活动: 1. 从半结构化数据中提取 JSON 字符串 2. 将有效的 JSON 字符串片段传递给 GSON 以将其解析为 Java 结构。首先,正则表达式是不够的,因为它不能执行“大括号匹配”,因此建议。

标签: java regex


【解决方案1】:

正如其他人所建议的,成熟的 JSON 解析器可能是要走的路。如果您想匹配上面简单示例中的键值对,您可以使用:

(?<=\{)\s*[^{]*?(?=[\},])

对于输入字符串

{title:'Title',  {data:'Data', {foo: 'Bar'}}}

这匹配:

 1. title:'Title'
 2. data:'Data'
 3. foo: 'Bar'

【讨论】:

  • 我应该试试!谢谢!
【解决方案2】:

这个递归 Perl/PCRE 正则表达式应该能够匹配任何有效的 JSON 或 JSON5 对象,包括嵌套对象和边缘情况,例如 JSON 字符串中的大括号或 JSON5 cmets:

/(\{(?:(?>[^{}"'\/]+)|(?>"(?:(?>[^\\"]+)|\\.)*")|(?>'(?:(?>[^\\']+)|\\.)*')|(?>\/\/.*\n)|(?>\/\*.*?\*\/)|(?-1))*\})/

当然,这有点难读,所以你可能更喜欢带注释的版本:

m{
  (                               # Begin capture group (matching a JSON object).
    \{                              # Match opening brace for JSON object.
    (?:                             # Begin non-capturing group to contain alternations.
      (?>[^{}"'\/]+)                  # Match a non-empty string which contains no braces, quotes or slashes, without backtracking.
    |                               # Alternation; next alternative follows.
      (?>"(?:(?>[^\\"]+)|\\.)*")      # Match a double-quoted JSON string, without backtracking.
    |                               # Alternation; next alternative follows.
      (?>'(?:(?>[^\\']+)|\\.)*')      # Match a single-quoted JSON5 string, without backtracking.
    |                               # Alternation; next alternative follows.
      (?>\/\/.*\n)                    # Match a single-line JSON5 comment, without backtracking.
    |                               # Alternation; next alternative follows.
      (?>\/\*.*?\*\/)                 # Match a multi-line JSON5 comment, without backtracking.
    |                               # Alternation; next alternative follows.
      (?-1)                           # Recurse to most recent capture group, to match a nested JSON object.
    )*                              # End of non-capturing group; match zero or more repetitions of this group.
    \}                              # Match closing brace for JSON object.
  )                               # End of capture group (matching a JSON object).
}x

【讨论】:

  • 这是对这个问题的认真回答吗?
  • 当然是认真的回答!这个正则表达式完全完成了问题所要求的:定位并提取嵌入在非 JSON 数据中的 JSON 对象,正确处理嵌套对象和边缘情况。然后可以将匹配项与 JSON 解析器一起使用以反序列化 JSON 对象。
  • 我对正则表达式不是很好,但这看起来很奇怪。无论如何,谢谢您的确认。
  • 我自己只是需要使用这个正则表达式,但我需要匹配隐藏在日志文件中的许多 JSON 对象,而这些对象原本是纯文本。它工作得很好,我只需要添加“/g”标志来获取所有匹配项而不是单个匹配项。
【解决方案3】:

感谢@Sanjay T. Sharma 向我指出“大括号匹配”,因为我最终对贪婪表达有了一些了解,也感谢其他人最初说我不应该做的事情。 幸运的是,结果证明使用贪婪的表达式是可以的

\\{\s*title.*\\}

因为右括号之间没有非 JSON 数据。

【讨论】:

    【解决方案4】:

    这绝对是可怕的,我不敢相信我真的把我的名字放到了这个解决方案中,但是你能不能找到 Javascript 块中的第一个 { 字符并尝试通过正确的 JSON 解析库?如果它有效,你就有了比赛。如果没有,请继续阅读直到下一个 { 字符并重新开始。

    那里有一些问题,但可能可以解决:

    • 您需要能够识别 Javascript 块。大多数语言都有 HTML 到 DOM 库(我是 Java 的 Cyberneko 的忠实粉丝),这使得关注 &lt;script&gt;...&lt;/script&gt; 块变得容易。
    • 您的 JSON 解析库需要在发现错误后立即停止使用流中的字符,并且在发生错误时不需要关闭流。

    当您找到第一个{ 后,改进方法是寻找匹配的}(一个简单的计数器,当您找到{ 时递增,当您找到@987654328 时递减@ 应该可以解决问题)。尝试将生成的字符串解析为 JSON。迭代直到它起作用或者你已经用完了可能的块。

    这是丑陋的、骇人听闻的,不应该将其用于生产代码。不过,我的印象是你只需要它来完成批处理作业,这就是我什至建议它的原因。

    【讨论】:

    • 对。这很难看,而且实施起来并不简单。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-08-29
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多