如何解析一个字符串与过滤器citeria在scala中,并使用它来过滤对象 [英] How to parse a string with filter citeria in scala and use it to filter objects

查看:137
本文介绍了如何解析一个字符串与过滤器citeria在scala中,并使用它来过滤对象的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

比方说,我有一个包含一些字符串,整数和枚举值的对象数组。还包含返回这些类型的这些类型和方法的数组。



例如一个包含下列ExampleObject的数组:

  object WeekDay继承Enumeration {type WeekDay = Value; val星期一,星期二,星期三,星期四,星期五,星期六,星期日=值} 

class ExampleObject(val integerValue1:Integer,val integerValue2:Integer,val stringValue1:String,val weekDay:WeekDay.Value, val integerArray:Array [Integer])
{def intReturningMethod1()= {0}}

在命令行中,我将一个带有过滤条件的字符串传递给scala应用程序。例如:

  -filter_criteria((integerValue1> 100 || integerValue2< 50)&&(stringValue1 = =A|| weekDay!=Mon))||(integerArray(15)== 1)

运算符应该在这些类型的值的正常if语句中执行您所期望的操作。



如何解析过滤条件字符串并使用它过滤来自数组的ExampleObjects?

我应该开始阅读,找出如何做到这一点?如果你想限制输入到一个有限的语言,你可以很容易地创建一个语言的解析器,只使用Scala核心库。



我已经为您的示例的精简版本做了这个

  object WeekDay扩展枚举{
类型WeekDay = Value; val星期一,星期二,星期三,星期四,星期五,星期六,星期日=星期二
}

case类ExampleObject(val integerValue1: {
def intReturningMethod1()= {0}
}

一个导入并创建一些助手:

  type FilterCriterion = ExampleObject =>布尔
类型提取器[T] = ExampleObject => T
$ b def compare [T <%Ordered [T]](v1:T,c:String,v2:T):Boolean = c match {
case< => v1< v2
案例> => v1> v2
case===> (v1:v2:任何):布尔值=(v1,v2)match {
case(s1: String,s2:String)=>比较(s1,c,s2)
case(i1:Int,i2:Int)=>比较(i1,c,i2)
的情况(w1:WeekDay.WeekDay,w2:WeekDay.WeekDay)=>比较(w1.id,c,w2.id)
case _ =>抛出新的IllegalArgumentException(s无法比较$ {v1.getClass}与$ {v2.getClass})
}

然后我创建解析器:

pre $ object $ Filter barser extends JavaTokenParsers {
def intExtractor:Parser [Extractor [Int]] = wholeNumber ^^ {s => Function.const(s.toInt)_} |
intReturningMethod1()^^^ {(e:ExampleObject)=> e.intReturningMethod1()} |
integerValue1^^^ {_.integerValue1}
def stringExtractor:Parser [Extractor [String]] = stringLiteral ^^ {s => Function.const(sdrop(1).dropRight(1))_} |
stringValue1^^^ {_stringValue1}
def weekDayExtrator:Parser [Extractor [WeekDay.WeekDay]] = stringLiteral ^? {
case s if WeekDay.values.exists(_。toString == s)=> Function.const(WeekDay.withName(s))_
}
def extractor:Parser [Extractor [Any]] = intExtractor | stringExtractor | weekDayExtrator
$ b $ def compareOp:Parser [FilterCriterion] =(extractor〜(<|==|>)〜extractor)^^ {
case v1〜c 〜v2 => (e:ExampleObject)=> compareAny(v1(e),c,v2(e))
}
$ b $ def def simpleExpression:Parser [FilterCriterion] =(〜> expression<〜) compareOp
def notExpression:Parser [FilterCriterion] =! 〜> simpleExpression ^^ {(ex)=> (e:ExampleObject)=> !ex(e)} |
simpleExpression
def andExpression:Parser [FilterCriterion] = repsep(notExpression,&&)^^ {(exs)=> (e:ExampleObject)=> (b,ex)=> b& amp; ex(e))}
def orExpression:Parser [FilterCriterion] = repsep(andExpression,||)^^ { (exs)=> (e:ExampleObject)=> exs.foldLeft(false)((b,ex)=> b || ex(e))}
def表达式:Parser [FilterCriterion] = orExpression

def parseExpressionString(s: String)= parseAll(expression,s)
}

这个解析器将你的输入字符串返回一个将ExampleObject映射到布尔值的函数。在分析输入字符串时,使用预定义的帮助器函数和解析器规则中定义的匿名函数构造该测试函数一次。输入字符串的解释只能在构造测试函数时进行一次。当您执行测试功能时,您将运行编译的Scale代码。所以它应该运行得非常快。

测试函数是安全的,因为它不允许用户运行任意的Scala代码。它只是通过解析器中提供的部分函数和预定义的帮助器来构造的。

  val parsedCriterion = FilterParser.parseExpressionString (((integerValue1> 100 || integerValue1< 50)&&(stringValue1 ==A))))

List(ExampleObject ),WeekDay.Mon),ExampleObject(2,B,WeekDay.Sat),ExampleObject(50,A,WeekDay.Mon))filter(parsedCriterion.get)

当您希望在您的ExampleObject中使用更多的函数或更多的字段时,您可以自己扩展解析器。

Lets say I have an array of objects that contains some String, Integer and Enum values. And also contains arrays of these types and methods that return these types.

For example an array containing the following ExampleObject:

object WeekDay extends Enumeration { type WeekDay = Value; val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value  }

class ExampleObject (val integerValue1 : Integer, val integerValue2 : Integer, val stringValue1 : String, val weekDay: WeekDay.Value, val integerArray : Array[Integer])
{  def intReturningMethod1()= {0}  }  

From the command line I pass in a string with filter criteria to the scala application. For example:

-filter_criteria "((integerValue1 > 100 || integerValue2 < 50) && (stringValue1 == "A" || weekDay != "Mon")) || (integerArray(15) == 1) "

The operators should do what you expect in a normal if statement with these types of values.

How can I parse the filter criteria string and use it to filter ExampleObjects from an array?

Or were should I start reading to find out how to do this?

解决方案

If you want to restrict the input to a limited language, you can easily create a parser for that language using only the Scala core library.

I have done this for a stripped down version of your example

  object WeekDay extends Enumeration {
    type WeekDay = Value; val Mon, Tue, Wed, Thu, Fri, Sat, Sun = Value
  }

  case class ExampleObject(val integerValue1 : Integer, val stringValue1 : String, val weekDay: WeekDay.Value){
    def intReturningMethod1()= {0}
  }

First I use an import and create some helpers:

  type FilterCriterion = ExampleObject => Boolean
  type Extractor[T] = ExampleObject => T

  def compare[T <% Ordered[T]](v1 : T, c : String, v2 : T) : Boolean = c match {
    case "<" => v1 < v2
    case ">" => v1 > v2
    case "==" => v1 == v2
  }

  def compareAny(v1: Any, c : String, v2 : Any) : Boolean = (v1,v2) match {
    case (s1: String, s2:String) => compare(s1,c,s2)
    case (i1: Int, i2 : Int) => compare(i1,c,i2)
    case (w1 : WeekDay.WeekDay, w2 : WeekDay.WeekDay) => compare(w1.id, c, w2.id)
    case _ => throw new IllegalArgumentException(s"Cannot compare ${v1.getClass} with ${v2.getClass}")
  }

Then I create the parser:

  object FilterParser extends JavaTokenParsers {
    def intExtractor : Parser[Extractor[Int]] = wholeNumber ^^ {s => Function.const(s.toInt)_} |
      "intReturningMethod1()" ^^^ {(e : ExampleObject) => e.intReturningMethod1()}  |
      "integerValue1" ^^^ {_.integerValue1}
    def stringExtractor : Parser[Extractor[String]] = stringLiteral ^^ {s => Function.const(sdrop(1).dropRight(1))_} |
      "stringValue1" ^^^ {_.stringValue1}
    def weekDayExtrator : Parser[Extractor[WeekDay.WeekDay]] = stringLiteral ^? {
      case s if WeekDay.values.exists(_.toString == s) => Function.const(WeekDay.withName(s))_
    }
    def extractor : Parser[Extractor[Any]] = intExtractor | stringExtractor | weekDayExtrator

    def compareOp : Parser[FilterCriterion] = (extractor ~ ("<"| "==" | ">") ~ extractor) ^^ {
      case v1 ~ c ~ v2 => (e : ExampleObject) => compareAny(v1(e),c,v2(e))
    }

    def simpleExpression : Parser[FilterCriterion] = "(" ~> expression <~ ")" | compareOp
    def notExpression : Parser[FilterCriterion] = "!" ~> simpleExpression ^^ {(ex) => (e : ExampleObject) => !ex(e)} |
      simpleExpression
    def andExpression : Parser[FilterCriterion] = repsep(notExpression,"&&") ^^ {(exs) => (e : ExampleObject) => exs.foldLeft(true)((b,ex)=> b && ex(e))}
    def orExpression : Parser[FilterCriterion] = repsep(andExpression,"||") ^^ {(exs) => (e : ExampleObject) => exs.foldLeft(false)((b,ex)=> b || ex(e))}
    def expression : Parser[FilterCriterion] = orExpression

    def parseExpressionString(s : String) = parseAll(expression, s)
  }

This parser takes your input string and returns a function that maps an ExampleObject to a boolean value. This test function is constructed once while parsing the input string, using the pre-defined helper functions and the anonymous functions defined in the parser rules. The interpretation of the input string is only done once, while constructing the test function. When you execute the test function, you will run compiled Scale code. So it should run quite fast.

The test function is safe, because it does not allow the user to run arbitrary Scala code. It will just be constructed from the partial function provided in the parser and the pre-defined helpers.

  val parsedCriterion=FilterParser.parseExpressionString("""((integerValue1 > 100 || integerValue1 < 50) && (stringValue1 == "A"))""")

  List(ExampleObject(1,"A", WeekDay.Mon), ExampleObject(2,"B", WeekDay.Sat), ExampleObject(50,"A", WeekDay.Mon)).filter(parsedCriterion.get)

You can easily extend the parser yourself, when you want it to use more functions or more fields in your ExampleObject.

这篇关于如何解析一个字符串与过滤器citeria在scala中,并使用它来过滤对象的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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