• 初试Scala解析XML


    使用Scala解析XML,充分体现了函数式编程的特点,简洁和明了。用Java去解析不是不行,只不过代码不够清晰明了。

    首先先把XML文件读入到内存里:

    val someXml = XML.loadFile("file/FIXExample.xml")

    这样someXml是一个scala.xml.Elem对象。

    Scala XML API提供了类似XPath的语法来解析XML。在NodeSeq这类父类里,定义了两个很重要的操作符(""和"\"),用来获得解析XML:

    • :Projection function, which returns elements of this sequence based on the string that--简单来说, 根据条件搜索下一子节点
    • \:Projection function, which returns elements of this sequence and of all its subsequences, based on the string that--而 \ 则是根据条件搜索所有的子节点

    先上一个XML的文件作为例子:

    <fix major="4" minor="2">
      <header>
        <field name="BeginString" required="Y">FIX4.2</field>
        <field name="MsgType" required="Y">Test</field>
      </header>
      <trailer>
        <field name="Signature" required="N"/>
        <field name="CheckSum" required="Y"/>
      </trailer>
      <messages>
        <message name="Logon" msgtype="A" msgcat="admin">
          <field name="ResetSeqNumFlag" required="N"/>
          <field name="MaxMessageSize" required="N"/>
          <group name="NoMsgTypes" required="N">
            <field name="RefMsgType" required="N"/>
            <field name="MsgDirection" required="N"/>
          </group>
        </message>
        <message name="ResendRequest" msgtype="2" msgcat="admin">
          <field name="BeginSeqNo" required="Y"/>
          <field name="EndSeqNo" required="Y"/>
        </message>
      </messages>
      <fields>
        <field number="1" name="TradingEntityId" type="STRING"/>
        <field number="4" name="AdvSide" type="STRING">
          <value enum="X" description="CROSS"/>
          <value enum="T" description="TRADE"/>
        </field>
        <field number="5" name="AdvTransType" type="STRING">
          <value enum="N" description="NEW"/>
        </field>
      </fields>
    </fix>

    1. 首先来个简单的,如果要找header下的field,那么这样写即可:

    val headerField = someXml"header""field"

    2.找所有的field:

    val field = someXml\"field"

    3. 找特定的属性(attribute),如找header下的所有field的name属性的值:

    val fieldAttributes = (someXml"header""field").map(_"@name")
    
    val fieldAttributes = someXml"header""field"\"@name"

    两个都能找到header下面所有field的name属性,但问题是输出的格式不一样。前者会返回一个List-List(BeginString, MsgType),而后者仅仅是BeginStringMsgType。中间连空格也没有。所以建议用前一种方法获得属性。

    之前以为,下面的方法,和第二种方法一样能够获得属性的值:

    val fieldAttributes = someXml"header""field""@name"

    根据操作符的定义,理论上应该可以输出name属性的。实际上输出的结果是空,什么也没有。

    操作符的源码里有这么一段:

        that match {
          case ""                                         => fail
          case "_"                                        => makeSeq(!_.isAtom)
          case _ if (that(0) == '@' && this.length == 1)  => atResult
          case _                                          => makeSeq(_.label == that)
        }

    第三个case表面,只有当this.length==1时,才可以这样做。原因其实很简单,somXml"header""field"返回的是一个Seq[Node]的集合,包含多个对象。而"@"的操作无法确定操作哪一个对象的属性:

      val x = <b><h id="bla"/><h id="blub"/></b>
      val y = <b><h id="bla"/></b>
      println(x\"h""@id") //Wrong
      println(y\"h""@id") //Correct with output: bla

    4. 查找并输出属性值和节点值的映射:

    (someXml"header""field").map(n=>(n"@name", n.text, n"@required"))

    这样的输出是List((BeginString,FIX4.2,Y), (MsgType,Test,Y))

    5. 有条件地查找节点,例如查找name=Logon的message:

    val resultXml1 = (someXml\"message").filter(_.attribute("name").exists(_.text.equals("Logon")))
    
    val resultXml2 = (someXml\"message").filter(x=>((x"@name").text)=="Logon")

    6. 通过 \"_" 获得所有的子节点,例如:

    println(resultXml1\"_")

    结果是:

    <message msgcat="admin" msgtype="A" name="Logon">
          <field required="N" name="ResetSeqNumFlag"/>
          <field required="N" name="MaxMessageSize"/>
          <group required="N" name="NoMsgTypes">
            <field required="N" name="RefMsgType"/>
            <field required="N" name="MsgDirection"/>
          </group>
    </message>
    <field required="N" name="ResetSeqNumFlag"/>
    <field required="N" name="MaxMessageSize"/>
    <group required="N" name="NoMsgTypes">
            <field required="N" name="RefMsgType"/>
            <field required="N" name="MsgDirection"/>
    </group>
    <field required="N" name="RefMsgType"/>
    <field required="N" name="MsgDirection"/>

    本文完

  • 相关阅读:
    前缀和
    B. Ilya and Queries
    BZOJ1652 [Usaco2006 Feb]Treats for the Cows
    NOIP2014提高组 酱油记
    NOIP初赛 BLESS ALL!
    BZOJ1096 [ZJOI2007]仓库建设
    BZOJ1036 [ZJOI2008]树的统计Count
    BZOJ1030 [JSOI2007]文本生成器
    BZOJ2749 [HAOI2012]外星人
    BZOJ1093 [ZJOI2007]最大半连通子图
  • 原文地址:https://www.cnblogs.com/techyc/p/3631107.html
Copyright © 2020-2023  润新知