【问题标题】:Regular expression to parse an array of JSON objects?解析JSON对象数组的正则表达式?
【发布时间】:2010-09-29 08:15:15
【问题描述】:

我正在尝试将 JSON 对象数组解析为 C# 中的字符串数组。我可以从 JSON 对象中提取数组,但无法将数组字符串拆分为单个对象的数组。

我拥有的是这个测试字符串:

string json = "{items:[{id:0,name:\"Lorem Ipsum\"},{id:1,name" 
            + ":\"Lorem Ipsum\"},{id:2,name:\"Lorem Ipsum\"}]}";

现在我正在使用以下正则表达式将项目拆分为单个对象。现在它们是 2 个单独的正则表达式,直到我用第二个解决问题:

Regex arrayFinder = new Regex(@"\{items:\[(?<items>[^\]]*)\]\}"
                                 , RegexOptions.ExplicitCapture);
Regex arrayParser = new Regex(@"((?<items>\{[^\}]\}),?)+"
                                 , RegexOptions.ExplicitCapture);

arrayFinder 正则表达式按我预期的方式工作,但由于我不明白的原因,arrayParser 正则表达式根本不起作用。我想要做的就是将各个项目拆分成它们自己的字符串,这样我就得到了一个这样的列表:

{id:0,name:"Lorem Ipsum"}
{id:1,name:"Lorem Ipsum"}
{id:2,name:"Lorem Ipsum"}

这个列表是string[] 数组还是GroupMatch 集合并不重要,但我对如何拆分对象感到困惑。使用上面声明的arrayParserjson 字符串,我已经尝试了这段代码,我认为它不会成功:

string json = "{items:[{id:0,name:\"Lorem Ipsum\"},{id:1,name" 
            + ":\"Lorem Ipsum\"},{id:2,name:\"Lorem Ipsum\"}]}";

Regex arrayFinder = new Regex(@"\{items:\[(?<items>[^\]]*)\]\}"
                                 , RegexOptions.ExplicitCapture);
Regex arrayParser = new Regex(@"((?<items>\{[^\}]\}),?)+"
                                 , RegexOptions.ExplicitCapture);

string array = arrayFinder.Match(json).Groups["items"].Value;
// At this point the 'array' variable contains: 
// {id:0,name:"Lorem Ipsum"},{id:1,name:"Lorem Ipsum"},{id:2,name:"Lorem Ipsum"}

// I would have expected one of these 2 lines to return 
// the array of matches I'm looking for
CaptureCollection c = arrayParser.Match(array).Captures;
GroupCollection g = arrayParser.Match(array).Groups;

谁能看出我做错了什么?我完全坚持这一点。

【问题讨论】:

  • json.org 上列出了几个用于 C#/.NET 的 JSON 解析器。为什么不使用其中之一?还是在挑战自己?
  • 部分是为了挑战自己,部分是因为这是一小段代码,我觉得在这种情况下使用第三方库有点矫枉过正。
  • 你如何衡量“矫枉过正”?如果您可以使用编写好的、经过测试的、可工作的代码,为什么不呢?
  • @Andy 在一个少于 100 行代码的项目中,如果没有必要,感觉应该避免需要外部 DLL。另外,就像我提到的那样,我也想要一些东西来挑战自己。
  • 我建议,与其凭感觉使用外部库,不如看看自己编写它与使用经过测试的工作代码的成本和风险。

标签: c# .net regex json


【解决方案1】:

您使用的是 .NET 3.5 吗?如果是这样,您可以使用DataContractJsonSerializer 来解析它。没有理由自己这样做。

如果你没有使用 .NET 3.5,你可以使用Jayrock

【讨论】:

  • 或 JavaScriptSerializer (3.5 SP1)
  • JavaScriptSerializer 也可以在 .NET 2.0 中使用,方法是安装 ASP.NET 2.0 AJAX Extensions 1.0,使用 System.Web.Extensions 程序集和 System.Web.Script.Serialization 命名空间
【解决方案2】:

平衡括号实际上是无法用正则表达式处理的语言的教科书示例。 JSON本质上是平衡的括号加上一堆其他东西,大括号被括号代替。在hierarchy of formal languages 中,JSON 是一种上下文无关的语言。正则表达式无法解析上下文无关语言。

一些系统提供对正则表达式的扩展,可以处理平衡的表达式。然而,它们都是丑陋的 hack,它们都是不可移植的,而且它们最终都不是适合这项工作的工具。

在专业工作中,您几乎总是会使用现有的 JSON 解析器。如果您想出于教育目的自己动手,那么我建议您从支持 + - * / ( ) 的简单算术语法开始。 (JSON 有一些转义规则,虽然并不复杂,但会使您的第一次尝试变得比实际需要的更难。)基本上,您需要:

  1. 将语言分解为符号字母表
  2. 根据可识别语言的符号编写上下文无关语法
  3. 将语法转换为乔姆斯基范式,或足够接近以简化第 5 步
  4. 编写一个词法分析器,将原始文本转换为您的输入字母表
  5. 编写一个递归下降解析器,获取词法分析器的输出,对其进行解析,然后生成某种输出

这是几乎任何一所大学的典型三年级 CS 作业。

下一步是找出在递归解析器中触发堆栈溢出所需的 JSON 字符串的复杂程度。然后看看其他可以编写的解析器类型,你就会明白为什么在现实世界中必须解析上下文无关语言的人使用 yacc 或 antlr 之类的工具而不是手动编写解析器。

如果这比您期待的更多,那么您应该随意使用现成的 JSON 解析器,并满足于您学到了一些重要且有用的东西:正则表达式的限制。

【讨论】:

  • Perl 正则表达式可以处理平衡括号,Perl 5.10 和 Perl6 更容易做到这一点。 perldoc.perl.org/perlretut.html#Recursive-patterns
  • 有趣的是,我听说手写比使用 flex、bison 或任何其他工具更容易。当然,说这句话的人写了几门现代编程语言,所以也许他不是一个很好的信息来源。
  • 我的意思是,他的功夫可能太棒了,不能成为外行人的良好信息来源。
  • 我不同意“在现实世界中必须解析上下文无关语言的任何人都使用 yacc 或 antlr 之类的工具而不是手动编写解析器”,这绝对不是真的。在许多情况下,手写词法分析器和解析器更容易创建。 JSON 就是其中一种情况。看:blog.aumcode.com/2013/11/working-with-json-data-in-nfx.html 整个事情是在 16 小时内编写的,包括单元测试,并且因为它使用“经典编译器设计”方法 Lexer->Parser->Object(在 JSON 的情况下),您可以动态进行模式匹配在 JSON 令牌的惰性流上,而不是字符串。
【解决方案3】:

JSON 通常不能用正则表达式解析(某些极其简化的 JSON 变体可以,但它们不是 JSON,而是其他东西)。

您需要一个实际的解析器来正确解析 JSON。

不管怎样,你为什么要尝试解析 JSON?有许多库可以为你做这件事,而且比你的代码要好得多。为什么要重新发明轮子,拐角处就有一家轮子厂,门上写着 FOSS 字样?

【讨论】:

    【解决方案4】:

    平衡括号实际上是无法用正则表达式处理的语言的教科书示例

    bla bla bla ... 看看这个:

    arrayParser = "(?<Key>[\w]+)":"?(?<Value>([\s\w\d\.\\\-/:_]+(,[,\s\w\d\.\\\-/:_]+)?)+)"?
    

    这对我有用

    如果要匹配空值,请将最后一个 '+' 更改为 '*'

    【讨论】:

    • 这很好,但它不包括 UTF 字符。
    • 我必须对其进行编辑以使其适用于我使用 DataContractJsonSerializer 序列化的 .NET 对象。具体来说,我为键添加了引号,并为值添加了可选引号: (?["\w"]+):?(?(["\s\w\d\.\\\-/ :_]+(,[,\s\w\d\.\\\-/:_]+)?)+)
    【解决方案5】:
    public Dictionary<string, string> ParseJSON(string s)
    {
        Regex r = new Regex("\"(?<Key>[\\w]*)\":\"?(?<Value>([\\s\\w\\d\\.\\\\\\-/:_\\+]+(,[,\\s\\w\\d\\.\\\\\\-/:_\\+]*)?)*)\"?");
        MatchCollection mc = r.Matches(s);
    
        Dictionary<string, string> json = new Dictionary<string, string>();
    
        foreach (Match k in mc)
        {
            json.Add(k.Groups["Key"].Value, k.Groups["Value"].Value);
    
        }
        return json;
    }
    

    这个函数实现了 Lukasz 正则表达式。我只将 inclide + char 添加到值组(因为我正在使用它来解析实时连接身份验证令牌)

    【讨论】:

      猜你喜欢
      • 2016-04-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-18
      • 1970-01-01
      • 1970-01-01
      • 2015-06-19
      • 2012-10-17
      相关资源
      最近更新 更多