代码之家  ›  专栏  ›  技术社区  ›  ScArcher2

如何将XML解析为“消息”,并使用流解析在scala中打印出来?

  •  3
  • ScArcher2  · 技术社区  · 14 年前

    现在我知道怎么做了 parse xml in scala as a stream 我需要帮助理解一个非常重要的例子。

    我希望将以下XML作为流进行分析,并在分析完完整的消息后发送消息(本例中打印到控制台)。

    我理解scala中基于流的解析使用case类来处理不同的元素,但是我只是开始了,我不太了解如何这样做。

    我在Java中使用了StAX解析器,我想把它翻译成Scala。

    任何帮助都将不胜感激。

    <?xml version="1.0" ?>
    <messages>
    <message>
       <to>john.doe@gmail.com</to>
       <from>jane.doe@gmail.com</from>
       <subject>Hi Nice</subject>
       <body>Hello this is a truly nice message!</body>
    </message>
    <message>
       <to>joe@gmail.com</to>
       <from>jane.doe@gmail.com</from>
       <subject>Hi Nice</subject>
       <body>Hello this is a truly nice message!</body>
    </message>
    </messages>
    
    1 回复  |  直到 14 年前
        1
  •  5
  •   huynhjl bhericher    14 年前

    这是2.8的。

    处理事件的典型方法是使用match语句。在我的例子中,我总是需要在处理元素时存储父元素(例如知道文本位于哪个标记中):

    import scala.xml.pull._
    import scala.io.Source
    import scala.collection.mutable.Stack
    
    val src = Source.fromString(xml)
    val er = new XMLEventReader(src)
    val stack = Stack[XMLEvent]()
    def iprintln(s:String) = println((" " * stack.size) + s.trim)
    while (er.hasNext) {
      er.next match {
        case x @ EvElemStart(_, label, _, _) =>
          stack push x
          iprintln("got <" + label + " ...>")
        case EvElemEnd(_, label) => 
          iprintln("got </" + label + ">")
          stack pop;
        case EvText(text) => 
          iprintln(text) 
        case EvEntityRef(entity) => 
          iprintln(entity) 
        case _ => // ignore everything else
      }
    }
    

    由于实体是事件,您可能需要转换为文本并将其与周围的文本组合。

    在上面的示例中,我只使用了标签,但您也可以使用 EvElemStart(pre, label, attrs, scope) 要提取更多内容,可以添加 if 在复杂条件下进行防护。

    如果你用的是2.7.x,我不知道 http://lampsvn.epfl.ch/trac/scala/ticket/2583 是反向移植的,因此,您可能会遇到与实体一起处理文本的问题。

    更重要的是,为了简洁起见,只需处理从头到尾的问题(尽管我不会称之为 斯卡拉路 ):

    class Message() {
      var to:String = _
      var from:String = _
      override def toString(): String = 
        "from %s to %s".format(from, to)
    }
    
    var message:Message = _
    var sb:StringBuilder = _
    
    while (er.hasNext) {
      er.next match {
        case x @ EvElemStart(_, "message", _, _) =>
          message = new Message
        case x @ EvElemStart(_, label, _, _) if
            List("to", "from") contains label =>
          sb = new StringBuilder 
        case EvElemEnd(_, "to") => 
          message.to = sb.toString
        case EvElemEnd(_, "from") => 
          message.from = sb.toString
          sb = new StringBuilder 
        case EvElemEnd(_, "message") => 
          println(message)
        case EvText(text) if sb != null => 
          sb ++= text
        case EvEntityRef(entity) => 
          sb ++= unquote(entity) // todo
        case _ => // ignore everything else
      }
    }