解析日志文件并转换为Scala中的案例类

时间:2017-07-03 14:51:35

标签: xml scala parsing

我要注意如何解决以下问题。 让我们说我有一个这样的日志文件:

asdasdçkpoiwqe
askdjadlskjqw
<stuff>
    <a>some val</a>
    <b>some val</b>
</stuff>
kasdjllasdj
clkj
skdjalkd
<moreStuff>
    <c>some val</c>
    <d>some val</d>
</moreStuff>
iuoudnas
salkdj
sdmlaks
<moreStuff>
    <c>more val</c>
    <d>some val</d>
 </moreStuff>
...

这是我有一些废话文本,在中间,一些xml结构很好。 我想解析这个文件并将这个xml转换为case类,所以我定义了:

case class Stuff(a: String, b: String)

case class MoreStuff(c: String, d: String)

和这段代码:

val filename = "logFile.log"
for (line <- Source.fromFile(filename).getLines) {
    line match {
              case "<stuff>" => parseStuff(line)
              case "<moreStuff>" => parseMoreStuff(line)
              case _ => println("Not Defined"+ line)
            }
}

def parseStuff(line: String) = {
  //Create a List[Stuff] 
}

def parseMoreStuff(line: String) = {
  //Create a List[Stuff]
}

但显然这不起作用,因为当匹配的周期时,传递给方法的唯一一行是<stuff><moreStuff>

比我想象的那样我可以将迭代器传递给方法并在make next里面。像这样:

def parseMoreStuff(line: String, it: Iterator) = {
  var l = line
  while(!line.equals("</moreStuff>")){
    l += line
    it.next()
}

现在我只有一个字符串l只有xml内容,我可以视为xml。我运行了这段代码,得到了java.util.NoSuchElementException: next on empty iterator,但无论如何我觉得这种方法很乱(即使我可以解决这个异常)。我不喜欢它,所以我的问题是,如果有一种更简洁的方法来解析具有这种特性的日志文件。

提前致谢

1 个答案:

答案 0 :(得分:1)

一种方法是首先忽略垃圾文本:

val xmlAsString =
  Source.fromFile(filename)
        .getLines
        .map(_.trim)
        .filter(_.startsWith("<"))
        .mkString

// <stuff><a>some val</a><b>some val</b></stuff><moreStuff><c>some val</c><d>some val</d></moreStuff><moreStuff><c>more val</c><d>some val</d></moreStuff>

请注意,在上面的代码中,我将Iterator转换为String,因此如果文件中的XML内容太大而无法容纳在内存中,则可能会出现问题。

接下来,使用Scala的标准XML库(从Scala 2.11开始,已经移动到它自己的library),将XML片段聚合到一个XML文档中(使这个复合文档格式良好,添加根元素):

import scala.xml._

val xmlDoc = XML.loadString("<stuffRoot>" + xmlAsString + "</stuffRoot>")

然后,要获得Seq StuffSeq MoreStuff s:

def parseStuff(node: Node): Stuff = {
  Stuff((node \ "a").toString, (node \ "b").toString)
}

def parseMoreStuff(node: Node): MoreStuff = {
  MoreStuff((node \ "c").toString, (node \ "d").toString)
}

val stuffs = (xmlDoc \ "stuff").map(parseStuff) // Seq[Stuff]
val moreStuffs = (xmlDoc \ "moreStuff").map(parseMoreStuff) // Seq[MoreStuff]