如何在C#中解析OData $ filter [英] How to Parse OData $filter in C#

查看:112
本文介绍了如何在C#中解析OData $ filter的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我该如何在后端操纵过滤器并想要过滤器查询参数的键值对?

How can i manipulate filter in the backend and want the key value pairs of the filter query parameters?

表达方式如下

?$ filter =((姓名eq'约翰'或姓名eq'Grace Paul')和(部门eq'财务和会计'))"

"?$filter =((Name eq 'John' or Name eq 'Grace Paul') and (Department eq 'Finance and Accounting'))"

由于有2个过滤器串联在一起,所以&我怎么能得到像这样的值

As there are 2 filters concatenated & how can i get the values like

Filter 1:
    Key: Name
    Operator: eq
    Value: Name

Operator: or

Filter 2:
    Key: Name
    Operator: eq
    Value: Grace Paul

Operator: and

Filter 3:
    Key: Department
    Operator: eq
    Value: Finance and Accounting

我尝试过

  • ODataUriParser ,但似乎在ASP.NET core 2.1 Web API中不支持.
  • 正则表达式-使用此
  • ODataUriParser, but it doesn't seems to support in ASP.NET core 2.1 web api.
  • Regular Expression - using this stack overflow question, it doesn't seem to work in my case as my 3rd filter contains and in the value & so the regular expression fails.
  • ODataQueryOptions in the method, but it gives the raw text where it cannot be extracted to the key value pairs like mentioned.

我正在将 ASP.NET Core 2.1 Web API与OData v4集成

有没有办法实现上述目标?

Is there a way to accomplish the above?

推荐答案

您可能要考虑定义自己的解析器,然后遍历 AST 以获得所需的值.有很多工具可以做到这一点(请参见 flex Irony 可能是一个可行的选择:它在.net standard 2.0中可用,而我没有插入.net core 2.1控制台测试项目时出现问题.

You may want to consider defining your own parser and then walking the AST to get desired values. There are plenty tools to do that (see flex or bison for example). But in .net world Irony might be a viable option: it's available in .net standard 2.0 which I had no issues plugging into a .net core 2.1 console test project.

要开始,通常需要定义一个语法.幸运的是,微软已经足够友善地向我们提供使用EBNF参考,因此我们要做的就是使其适应Irony.最后,我实现了上面语法的一个子集,该子集似乎可以满足您的示例语句的要求(以及其他一点,您可以随意删节).

To start off, you normally need to define a grammar. Luckily, Microsoft have been kind enough to supply us with EBNF reference so all we have to do is to adapt it to Irony. I ended up implementing a subset of the grammar above that seems to cater for your example statement (and a bit above and beyond, feel free to cut it down).

using Irony.Parsing;

namespace irony_playground
{
    [Language("OData", "1.0", "OData Filter")]
    public class OData: Grammar
    {
        public OData()
        {
            // first we define some terms
            var identifier = new RegexBasedTerminal("identifier", "[a-zA-Z_][a-zA-Z_0-9]*");
            var string_literal = new StringLiteral("string_literal", "'");
            var integer_literal = new NumberLiteral("integer_literal", NumberOptions.IntOnly);
            var float_literal = new NumberLiteral("float_literal", NumberOptions.AllowSign|NumberOptions.AllowSign) 
                                        | new RegexBasedTerminal("float_literal", "(NaN)|-?(INF)");
            var boolean_literal = new RegexBasedTerminal("boolean_literal", "(true)|(false)");

            var filter_expression = new NonTerminal("filter_expression");
            var boolean_expression = new NonTerminal("boolean_expression");
            var collection_filter_expression = new NonTerminal("collection_filter_expression");
            var logical_expression = new NonTerminal("logical_expression");
            var comparison_expression = new NonTerminal("comparison_expression");
            var variable = new NonTerminal("variable");
            var field_path = new NonTerminal("field_path");
            var lambda_expression = new NonTerminal("lambda_expression");
            var comparison_operator = new NonTerminal("comparison_operator");
            var constant = new NonTerminal("constant");

            Root = filter_expression; // this is where our entry point will be. 

            // and from here on we expand on all terms and their relationships
            filter_expression.Rule = boolean_expression;

            boolean_expression.Rule = collection_filter_expression
                                      | logical_expression
                                      | comparison_expression
                                      | boolean_literal
                                      | "(" + boolean_expression + ")"
                                      | variable;
            variable.Rule = identifier | field_path;

            field_path.Rule = MakeStarRule(field_path, ToTerm("/"), identifier);

            collection_filter_expression.Rule =
                field_path + "/all(" + lambda_expression + ")"
                | field_path + "/any(" + lambda_expression + ")"
                | field_path + "/any()";

            lambda_expression.Rule = identifier + ":" + boolean_expression;

            logical_expression.Rule =
                boolean_expression + (ToTerm("and", "and") | ToTerm("or", "or")) + boolean_expression
                | ToTerm("not", "not") + boolean_expression;

            comparison_expression.Rule =
                variable + comparison_operator + constant |
                constant + comparison_operator + variable;

            constant.Rule =
                string_literal
                | integer_literal
                | float_literal
                | boolean_literal
                | ToTerm("null");

            comparison_operator.Rule = ToTerm("gt") | "lt" | "ge" | "le" | "eq" | "ne";

            RegisterBracePair("(", ")");
        }
    }
}

一点提示:Irony带有语法浏览器工具,该工具可让您加载语法dll并对其进行调试,因此建议您将类放在自己的项目中.这样一来,您就可以更轻松地将时间花在围绕这些概念上:

A bit of a hint: Irony comes with Grammar Explorer tool that allows you to load grammar dlls and debug with them, so I'd suggest you put your class in its own project. Then you would have easier time wrapping your head around the concepts:

对语法满意后,需要从项目中引用它并解析输入字符串:

after you're happy with the grammar, you need to reference it from your project and parse the input string:

class Program
{
    static void Main(string[] args)
    {
        var g = new OData();
        var l = new LanguageData(g);
        var r = new Parser(l);
        var p = r.Parse("((Name eq 'John' or Name eq 'Grace Paul') and (Department eq 'Finance and Accounting'))"); // here's your tree
        // this is where you walk it and extract whatever data you desire 
    }
}

然后,您要做的就是遍历结果树并根据sytax节点类型应用自定义逻辑.如何执行此操作的示例可在此SO中找到答案.

Then, all you have to do is walk the resulting tree and apply your custom logic based on sytax node type. One example how to do that can be found in this SO answer.

根据您的要求,您可能会发现这完全是您的目标,或者实际上会发现它给您的控制级别是完全正确的.

Depending on your requirements, you might find this is going to be a total overkill for your purpose, or might actually find that level of control it gives you is exactly right.

这篇关于如何在C#中解析OData $ filter的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆