假设我有一个包含一些String,Integer和Enum值的对象数组。并且还包含返回这些类型的这些类型和方法的数组。
例如,包含以下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} }
从命令行我将带有过滤条件的字符串传递给scala应用程序。例如:
-filter_criteria "((integerValue1 > 100 || integerValue2 < 50) && (stringValue1 == "A" || weekDay != "Mon")) || (integerArray(15) == 1) "
运算符应该在具有这些类型值的普通if语句中执行您期望的操作。
如何解析过滤条件字符串并使用它来过滤数组中的ExampleObjects?
或者我应该从哪里开始阅读以了解如何执行此操作?
答案 0 :(得分:3)
如果要将输入限制为有限的语言,只需使用Scala核心库就可以轻松地为该语言创建解析器。
我已经为你的例子
的精简版做了这个 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}
}
首先我使用导入并创建一些助手:
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}")
}
然后我创建解析器:
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(s.drop(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)
}
此解析器获取您的输入字符串并返回一个将ExampleObject映射到布尔值的函数。使用预定义的辅助函数和解析器规则中定义的匿名函数,在解析输入字符串时构造此测试函数一次。在构造测试函数时,输入字符串的解释只进行一次。执行测试功能时,将运行已编译的Scale代码。所以它应该运行得非常快。
测试功能是安全的,因为它不允许用户运行任意Scala代码。它只是由解析器中提供的部分函数和预定义的帮助器构建。
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)
当您希望它在ExampleObject中使用更多函数或更多字段时,您可以自己扩展解析器。
答案 1 :(得分:1)
您可能想看看Twitter的Eval实用程序库,您可以find here on GitHub。您可以将传入的过滤逻辑替换为您想要使用它的点,并将其传递给eval函数。