我正在使用Scala的Parser Combinators来解析字符串(没有换行符,人为举例)。
字符串由许多不同的部分组成,我想分别提取并填充案例类。
case class MyRecord(foo: String, bar: String, baz: String, bam: String, bat: String)
object MyParser extends scala.util.parsing.combinator.RegexParsers {
val foo: Parser[String] = "foo"
val bar: Parser[String] = "bar"
val baz: Parser[String] = "baz"
val bam: Parser[String] = "bam"
val bat: Parser[String] = "bat"
val expression: Parser[MyRecord] =
foo ~ bar ~ baz ~ bam ~ bat ^^ {
case foo ~ bar ~ baz ~ bam ~ bat => MyRecord(foo, bar, baz, bam, bat)
}
}
这非常有效,但是有没有办法将匹配结果的各个部分直接应用到案例类而不解构?
val expression: Parser[MyRecord] =
foo ~ bar ~ baz ~ bam ~ bat ^^ MyRecord
更多信息:我解析的字符串很长很复杂(实际上,它是一个充满长复杂字符串的整个文件)所以改为regexp是不合适的问题。
答案 0 :(得分:5)
可以使用Shapeless2库。对于给定的:
foldRight
您可以使用~
的通用 import shapeless._
object f extends Poly2 {
implicit def parser[T, U <: HList] =
at[Parser[T], Parser[U]]{(a, b) =>
for {aa <- a; bb <- b} yield aa :: bb
}
}
val p: Parser[Record] = (foo :: bar :: car :: HNil)
.foldRight(success(HNil))(f).map(Generic[Record].from)
intead组合解析器:
scala> parseAll(p, "foo bar car").get
res50: Record = Record(foo,bar,car)
结果:
~
P.S。内置scala功能的问题在于它们构建了基于::
的类型化二叉树,这种树难以遍历并变为元组。 Shapeless解决了这个问题 - 它有自己的基于HList
的二叉树,名为foldLeft
,它是相似的但有一些有趣的操作,比如转换为元组或案例类(可能是基于宏的)。在这个例子中,我使用flatMap
来构建Shapeless-hlist和for-comprehension(在解析器上扩展为foldLeft
)以组合解析器,因为它们具有monadic性质。在无形状中,您必须将T
的处理程序定义为通用含义集,它可以处理通用输入(如U
或f
)。
你可以重用我的implicit class as2[A, B](t: Parser[A ~ B]){ def ^^^^[T] (co: (A, B) => T) = t map {tt => val (a ~ b) = tt; co(a, b)} }
implicit class as3[A, B, C](t: Parser[A ~ B ~ C]){ def ^^^^[T] (co: (A, B, C) => T) = t map {tt => val (a ~ b ~ c) = tt; co(a, b, c)} }
...
implicit class as21 ...
对象以类型安全的方式组合任何解析器(你可以在这里组合甚至不同的类型 - 没关系)。
其次,不太通用的方式是:
scala> val p = foo ~ bar ~ car ^^^^ Record
p: MyParser.Parser[Record] = Parser ()
scala> parseAll(p, "foo bar car").get
res53: Record = Record(foo,bar,car)
用法:
public class MyHandler extends DefaultHandler {
// Use a DEQUE to track the current position inthe xml.
private Deque<String> position = new ArrayDeque<>();
// My data.
private StringBuilder data = new StringBuilder();
private String element = null;
@Override
public void characters(char[] ch, int start, int length) throws SAXException {
if (match()) {
// Append to my buffer.
data.append(ch, start, length);
}
}
@Override
public void endElement(String uri, String localName, String qName) throws SAXException {
// Ending a tag - pop it from end.
position.removeLast();
}
@Override
public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException {
// Starting a tag - push it at the end.
position.addLast(qName);
}
public String getElement() {
return this.element;
}
// Specifically looking for the REQUEST.ELEMENT - not the REQUEST.LIST.ELEMENT
private final String[] lookingFor = {"REQUEST", "ELEMENT"};
private boolean match() {
// Must be that deep.
if (position.size() == lookingFor.length) {
// Must match.
Iterator<String> match = position.iterator();
for (int i = 0; i < lookingFor.length; i++) {
// Match?
if (!match.next().equals(lookingFor[i])) {
return false;
}
}
} else {
// Wrong depth.
return false;
}
// No mismatch -> match!
return true;
}
}
这不是很酷,但不需要外部库。