比方说,我有对象的数组,其中包含一些字符串,整数和枚举值。 而且还含有这些类型和方法返回这些类型的数组。
例如包含数组以下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} }
从命令行我通过在与过滤器标准来阶应用的字符串。 例如:
-filter_criteria "((integerValue1 > 100 || integerValue2 < 50) && (stringValue1 == "A" || weekDay != "Mon")) || (integerArray(15) == 1) "
运营商应该做你在一个正常的期望是什么,如果这些类型的值的声明。
如何可以解析过滤条件字符串,并用它来从一个数组过滤ExampleObjects?
或者我应该在哪里开始阅读,找出如何做到这一点?
如果你想限制输入到有限的语言,你可以轻松地创建仅使用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一个布尔值的函数。 在解析输入字符串,利用预先定义的辅助函数,并在解析器规则中定义的匿名函数此测试功能被构造一次。 输入字符串的解释只进行一次,而构建的测试功能。 当您执行的测试功能,您将运行编译的代码规模。 所以它应该运行相当快。
该测试功能是安全的,因为它不允许用户运行任意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使用更多的功能或多个字段。
你可能想看看Twitter的评估和演示工具库,你可以在这里找到在GitHub上 。 你可以只替换在过滤逻辑放到你想使用它,并把它传递给eval函数点的String传递。
文章来源: How to parse a string with filter criteria in scala and use it to filter objects