用正则表达式解析JSON对象数组的方法?

11
我正在尝试将一个JSON对象数组解析为C#中的字符串数组。我可以从JSON对象中提取数组,但我无法将数组字符串拆分为单个对象数组。这是我的测试字符串:
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);
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;

有人能看出我哪里做错了吗?我完全卡住了。


在http://www.json.org/上列出了几个C#/.NET的JSON解析器。为什么不使用其中之一呢?或者你是在挑战自己吗? - strager
部分是为了挑战自己,部分是因为这段代码非常简短,我觉得使用第三方库有点杀鸡焉用牛刀。 - Dan Herbert
1
你如何衡量“过度设计”?如果你可以使用经过编写、测试和工作的代码,为什么不这样做呢? - Andy Lester
4
在不到100行代码的项目中,如果没有必要,需要外部DLL似乎应该避免使用。另外,像我之前提到的,我也想找点有挑战性的事情来尝试一下。 - Dan Herbert
2
我建议您不要凭感觉使用外部库,而是应该考虑编写自己的代码与使用经过测试、可靠的代码之间的成本和风险。 - Andy Lester
5个回答

40
平衡的括号是一个不能用正则表达式处理的语言的教科书例子。JSON 本质上是平衡的括号加上一堆其他东西,其中花括号被括号替换了。在形式语言的层次结构中,JSON 是一种上下文无关语言。正则表达式无法解析上下文无关语言。
有些系统提供了扩展正则表达式的功能,可以“半吊子地”处理平衡的表达式。但它们都是丑陋的黑客行为,都不可移植,而且最终都不适合此工作。
在专业工作中,你几乎总是会使用现有的 JSON 解析器。如果你想为教育目的自己开发,则建议从支持 + - * / ( ) 的简单算术语法开始。 (JSON 有一些转义规则,虽然不复杂,但会使您的第一次尝试比必要的困难。)基本上,您需要:
1. 将语言分解成符号字母表 2. 用这些符号编写一个上下文无关文法来识别该语言 3. 将文法转换为 Chomsky 正常形式,或者足够接近以便于轻松完成步骤5 4. 编写一个词法分析器,将原始文本转换为您的输入符号字母表 5. 编写一个递归下降解析器,获取词法分析器的输出,对其进行解析并生成某种输出
这是几乎任何大学的典型三年级计算机科学作业。
下一步是找出需要多复杂的JSON字符串才能触发递归解析器中的堆栈溢出。然后看看其他类型的解析器可以编写,你就会明白为什么在现实世界中必须解析上下文无关语言的人使用像yacc或antlr这样的工具而不是手动编写解析器。如果这比您想学习的更多,则可以随意使用现成的JSON解析器,满足您已经学到了重要和有用的东西:正则表达式的限制。

Perl正则表达式可以处理平衡括号,Perl 5.10和Perl6使这个过程更加容易。http://perldoc.perl.org/perlretut.html#Recursive-patterns - Brad Gilbert
有趣的是,我听说手写代码比使用flex、bison或其他任何工具更容易。当然,这个人说过他参与编写了几种现代编程语言,所以也许他不是一个好的信息来源。 - Mainguy
我的意思是,他的功夫可能太高超了,不适合作为普通人获取信息的好来源。 - Mainguy
我不同意“在现实世界中必须解析上下文无关语言的任何人都应该使用类似yacc或antlr的工具而不是手写解析器”的说法,这绝对不是真的。在许多情况下,手写词法分析器和解析器更容易创建。JSON就是其中之一。看看这个:http://blog.aumcode.com/2013/11/working-with-json-data-in-nfx.html 整个过程包括单元测试在内只用了<16小时,并且因为它使用“经典编译器设计”方法Lexer->Parser->Object(在JSON的情况下),所以您可以在JSON令牌的惰性流上即时进行模式匹配,而不是字符串。 - itadapter DKh

13
平衡括号是一个不能用正则表达式处理的语言的教科书例子。
嘿,看这个...
arrayParser = "(?<Key>[\w]+)":"?(?<Value>([\s\w\d\.\\\-/:_]+(,[,\s\w\d\.\\\-/:_]+)?)+)"?

这对我有用

如果您想匹配空值,请将最后一个“+”改为“*”


很好,但它不支持UTF字符。 - Serkan Yersen
1
我不得不编辑它,以使其适用于我使用DataContractJsonSerializer序列化的.NET对象。具体来说,我为键添加了引号,并为值添加了可选引号:(?<Key>["\w"]+):?(?<Value>(["\s\w\d.\-/:]+(,[,\s\w\d.\-/:]+)?)+) - rohancragg

6

你是否在使用.NET 3.5?如果是,你可以使用DataContractJsonSerializer来解析。没有必要自己动手。

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


1
或者 JavaScriptSerializer(3.5 SP1) - Marc Gravell
3
JavaScriptSerializer也可以在.NET 2.0中使用,方法是安装ASP.NET 2.0 AJAX扩展1.0,使用System.Web.Extensions程序集和System.Web.Script.Serialization命名空间。 - Russ Cam

2
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正则表达式。我只是添加了包含+字符到值组中的内容(因为我正在使用它来解析Live Connect授权令牌)。


0

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

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

无论如何,为什么要尝试解析JSON?有许多库可以为您完成此操作,并且比您的代码更好。为什么要重新发明轮子,当角落里有一个写着FOSS的轮子工厂呢?


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接