如何在 C# 中拆分 OData 多级展开查询字符串?

Posted

技术标签:

【中文标题】如何在 C# 中拆分 OData 多级展开查询字符串?【英文标题】:How to Split OData multi-level expand query string in C#? 【发布时间】:2021-08-28 03:50:06 【问题描述】:

我有一个网址:Expand=User($select=Firstname,Lastname),Organisation,Contract($Expand=MyOrganisation($select=Name,Status),Organisation),List

我需要按以下格式拆分此字符串:

User($select=Firstname,Lastname)

Organisation

Contract($Expand=MyOrganisation($select=Name,Status),Organisation)

List

如何在 C# 中实现这个功能?

【问题讨论】:

您是否有任何理由使用单个参数而不是 4 个参数?比如user=User($select=Firstname,Lastname)&organisation=Organisation等 是的,需要进行 odata 转换。 OData 是这里的东西 "$select=Firstname,Lastname",它不需要把所有东西都放在一个字符串中 Odata 支持多级过滤和扩展功能 例如:***.com/questions/51525409/… 【参考方案1】:

您不仅需要拆分字符串,还需要在拆分时跟踪括号。仅使用普通的旧正则表达式是不可能的。见this post。 但是,可以通过一些高级的 RegEx 来实现拆分; .NET 幸运地支持balancing groups,您可以使用它来跟踪括号。 This answer 对提出解决方案很有帮助。为了便于阅读,我将正则表达式拆分为多行并使用了RegexOptions.IgnorePatternWhitespace

string url = "User($select=Firstname,Lastname),Organisation,Contract($Expand=MyOrganisation($select=Name,Status),Organisation),List";
Regex rgx = new Regex(
               @"(.+?)
                (
                    (
                        \(
                            (?:
                                [^()]
                                |
                                (?'open'\()
                                |
                                (?'close-open'\))
                            )+
                            (?(open)(?!))
                        \)
                    )
                    ,
                    |
                    ,
                    |
                    \b$
                )", 
               RegexOptions.IgnorePatternWhitespace);

foreach(var match in rgx.Matches(url))

    Console.WriteLine($"match.Groups[1] match.Groups[3]"); 

该字段将以match.Groups[1] 的形式提供,参数(如果有)将以match.Groups[3] 的形式提供(如果没有参数,这将是一个空字符串)。您可以访问match.Groups[0] 获取整个群组。

正则表达式分解

Regex Description
(.+?) Non-greedily match one or more characters
\( and \) Match an actual ( and )
[^()] Match any character that is not a ( or )
(?'open'\() Create a named group with the name "open" and match a ( character
(?'close-open\)) Create a group "close" and assign the interval between "open" and "close" to "close" and delete group "open"
(?(open)(?!)) Assert if the "open" group is not deleted
(?:[^()]|(?'open'\()|(?'close-open'\)))+ Create a non-capturing group and match one or more characters that match one of the expressions between |

【讨论】:

列表将不是结果的一部分并且 match.Groups[2] 将崩溃 预期:字段:合同参数:$Expand=MyOrganisation($select=Name,Status),Organisation) @AmalK 在合同字符串拆分中没有预期请给我这个想法。 完美正则表达式解释:) @AmalK 和 Boris Sokolov 这很好。非常感谢您的支持。【参考方案2】:

您更有可能必须使用带有内置URI Parser 的ODataLib

Uri requestUri = new Uri("Products?$select=ID&$expand=ProductDetail" +
                         "&$filter=Categories/any(d:d/ID%20gt%201)&$orderby=ID%20desc" +
                         "&$top=1&$count=true&$search=tom",
                         UriKind.Relative);
ODataUriParser parser = new ODataUriParser(model, serviceRoot, requestUri);
SelectExpandClause expand = parser.ParseSelectAndExpand(); // parse $select, $expand
FilterClause filter = parser.ParseFilter();                // parse $filter
OrderByClause orderby = parser.ParseOrderBy();             // parse $orderby
SearchClause search = parser.ParseSearch();                // parse $search
long? top = parser.ParseTop();                             // parse $top
long? skip = parser.ParseSkip();                           // parse $skip
bool? count = parser.ParseCount();                         // parse $count

添加 RegExp 选项(Amal 在下面提供的固定版本)

    string url = "User($select=Firstname,Lastname),Organisation,Contract($Expand=MyOrganisation($select=Name,Status),Organisation),List";

    Regex rgx = new Regex(@"(.+?)(?:(\(.*?\)),|,)");

    foreach (var match in rgx.Matches($"url,"))
    
        Console.WriteLine(match.ToString()[..^1]);
    

【讨论】:

我们没有使用动态或 Jobject 的模型。我试过没有模型就不行。 手动解析会有问题,因为您没有特定的分隔符(逗号用于多种用途)。因此,如果您确定有 ")," 语句,然后是 "," 和 ")," - 您可以使用 IndexOf() 和 substrigns 这是使用索引和范围的好方法。请注意,它们仅从 C# 8.0 开始受支持

以上是关于如何在 C# 中拆分 OData 多级展开查询字符串?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C# 为 OData 查询中指定的每个过滤器获取一组键/值对?

如何在 C# 操作中读取 OData URL 参数值?

从表中仅选择几列以使用 C# Web API 上的 Odata Select 查询进行映射

如何在 C# 中使用 JSON 反序列化 oData V2?

如何从 C# 控制器(OData)的 JSON 序列化中修复丢失的子对象(导航属性)?

如何在C#中将带逗号的字符串拆分为两个字符串[重复]