【问题标题】:Fixing a bad JSON grammar修复错误的 JSON 语法
【发布时间】:2012-01-02 06:32:56
【问题描述】:

我刚开始学习解析,我在 Haskell 中写了this simple parser(使用 parsec)来读取 JSON 并为其构建一个简单的树。我正在使用RFC 4627中的语法。

但是,当我尝试解析字符串 {"x":1 } 时,我得到了输出:

解析错误(第 1 行,第 8 列):
意外的“}”
期待空白字符或“,”

这似乎只发生在我在右大括号 (]) 或小胡子 (}) 前有空格时。

我做错了什么?如果我在结束符号之前避免空格,它会完美运行。

【问题讨论】:

  • 有点不相关:pastebin 的语法高亮并不出色。实际上有一个 haskell 版本的 pastebin:hpaste.org

标签: json parsing haskell parsec


【解决方案1】:

Parsec 不会自动进行倒带和回溯。当您编写sepBy member valueSeparator 时,valueSeparator 会占用空格,因此解析器会像这样解析您的值:

{"x":1 }
[------- object
%        beginObject
 [-]     name
    %    nameSeparator
     %   jvalue
      [- valueSeparator
       X In valueSeparator: unexpected "}"

Legend:
[--]     full match
%        full char match
[--      incomplete match
X        incomplete char match

valueSeparator 失败时,Parsec 不会返回并尝试不同的解析组合,因为一个字符已经在valueSeparator 中匹配。

您有两种选择来解决您的问题:

  1. 由于空格在 JSON 中是微不足道的,因此始终在重要标记之后使用空格,而不是之前。所以,tok 应该只消耗字符后面的空格,所以它的定义是tok c = char c *> ws(*>) 来自Control.Applicative);将相同的规则应用于所有其他解析器。由于以这种方式输入“错误的解析器”后您将永远不会消耗空白空间,因此您最终不必回溯。
  2. 在 Parsec 中使用回溯,方法是在解析器前面添加 try,解析器可能会消耗多个字符,并且如果解析失败应该回退输入。

编辑:更新了 ASCII 图形以使其更有意义。

【讨论】:

  • 解决此问题的最佳方法(代码内)是什么?在这种情况下我应该放弃 sepBy 吗?
  • 另外,我在令牌之前和之后读取空格的原因是因为这就是它在 RFC 中所说的 =/
  • 如果您选择我建议的两个选项之一,sepBy 很好。具体来说,member 'sepBy' try valueSeparator 应该按预期工作(将 ' 替换为 `)。
  • 耶!有用!非常感谢。我现在脸上挂着最可笑的笑容。 :) 关于在哪里使用或不使用 try 的任何建议?我从来没有设法完全理解它。
  • @Clark Gaebel - 尝试仅将try 用于字符级解析器。如果您按照 Tikhon Jelvis 的建议使用 ParsecToken 模块,那么对 try 的需求就会消失。也就是说,对于 CharParsers,您可能仍然需要 try - 这些 Parsec 相当于扫描仪的令牌。
【解决方案2】:

一般的解决方案是让所有解析器跳过尾随空格。查看 Parsec 文档中的 lexeme(在 ParsecToken 中),以获得一种巧妙的方法来做到这一点,或者自己制作一个简单的版本:

 lexeme parser = do result <- parser
                    spaces
                    return result

然后在你的所有标记上使用这个函数(比如数字文字)。这样,您只需要担心表达式开头的空格。

有关ParsecToken 和朋友的更多信息,请查看Parsec docs 的“词法分析”部分。

只跳过一个标记后的空格是有意义的,除非在开头可以手动跳过它。即使您最终没有使用ParsecToken 模块,您也应该采用这种方法。

您似乎已经拥有tok,它的作用类似于我的lexeme,只是它在两边 都消耗空格。将其更改为仅在令牌 之后使用空格,并手动忽略输入开头的空格。这应该(理想情况下:))解决问题。

【讨论】:

  • 我的代码在pastebin.com/ewGH7QMh。在我最初的问题中单击“这个简单的解析器”。
  • 是的,我第一次阅读问题时错过了链接。读完后,我可能会大量修改我的答案。
  • 啊。我之前或多或少遇到过同样的问题,基本上我就是这样解决的,但我的实际代码却大不相同。
猜你喜欢
  • 2023-01-30
  • 2013-11-23
  • 1970-01-01
  • 1970-01-01
  • 2015-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多