2011-06-21 38 views
3

我將一些xml映射到一個case類,它工作正常,但我有一種感覺,我的命令性blinkers正在盲目我更好的功能解決方案。任何人都可以提出比這更好的辦法:xml解析的更多功能方法

def buildAddress(geocodeResponse: NodeSeq) : Address = { 
    val addressNodes = geocodeResponse \\ "address_component" 
    var street = " " 
    var town = "" 
    var suburb = "" 
    var province = "" 
    var country = "" 
    var postalCode = "" 

    addressNodes.foreach {node =>  
    val typeString = (node \ "type").head.text 
    if ("street_number" == typeString) { 
     street = (node \ "long_name").text + street 
    } 
    else if ("route" == typeString) { 
     street = street + (node \ "long_name").text 
    } 
    else if ("locality" == typeString) { 
     town = (node \ "long_name").text 
    } 
    else if ("sublocality" == typeString) { 
     suburb = (node \ "long_name").text 
    } 
    else if ("administrative_area_level_1" == typeString) { 
     province = (node \ "long_name").text 
    } 
    else if ("country" == typeString) { 
     country = (node \ "long_name").text 
    } 
    else if ("postal_code" == typeString) { 
     town = (node \ "long_name").text 
    } 
    } 
    Address(street,suburb,town,province,country,postalCode) 
} 

在這種情況下,XML檢索從他谷歌的地理編碼API,使用調度。

import dispatch._ 
    def lookupAddress(lat: Double, long: Double): Address = { 
    val position = lat.toString + "," + long.toString 
    val req = url("http://maps.googleapis.com/maps/api/geocode/xml") <<? Map("latlng" -> position, "sensor" -> "true") 
    Http(req </> { 
    nodes => buildAddress(nodes) 
    }) 
    } 

XML結果看起來是這樣的:

<GeocodeResponse> 
     <status>OK</status> 
     <result> 
     <type>street_address</type> 
     <formatted_address>3 Louw St, Stellenbosch 7600, South Africa</formatted_address> 
     <address_component> 
      <long_name>3</long_name> 
      <short_name>3</short_name> 
      <type>street_number</type> 
     </address_component> 
     <address_component> 
      <long_name>Louw St</long_name> 
      <short_name>Louw St</short_name> 
      <type>route</type> 
     </address_component> 
     <address_component> 
      <long_name>Stellenbosch Central</long_name> 
      <short_name>Stellenbosch Central</short_name> 
      <type>sublocality</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>Stellenbosch</long_name> 
      <short_name>Stellenbosch</short_name> 
      <type>locality</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>Stellenbosch</long_name> 
      <short_name>Stellenbosch</short_name> 
      <type>administrative_area_level_3</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>Brede River DC</long_name> 
      <short_name>Brede River DC</short_name> 
      <type>administrative_area_level_2</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>Western Cape</long_name> 
      <short_name>WC</short_name> 
      <type>administrative_area_level_1</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>South Africa</long_name> 
      <short_name>ZA</short_name> 
      <type>country</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>7600</long_name> 
      <short_name>7600</short_name> 
      <type>postal_code</type> 
     </address_component> 
     <geometry> 
      <location> 
      <lat>-33.9403990</lat> 
      <lng>18.8610090</lng> 
      </location> 
      <location_type>ROOFTOP</location_type> 
      <viewport> 
      <southwest> 
       <lat>-33.9435466</lat> 
       <lng>18.8578614</lng> 
      </southwest> 
      <northeast> 
       <lat>-33.9372514</lat> 
       <lng>18.8641566</lng> 
      </northeast> 
      </viewport> 
     </geometry> 
     </result> 
     <result> 
     <type>sublocality</type> 
     <type>political</type> 
     <formatted_address>Stellenbosch Central, Stellenbosch, South Africa</formatted_address> 
     <address_component> 
      <long_name>Stellenbosch Central</long_name> 
      <short_name>Stellenbosch Central</short_name> 
      <type>sublocality</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>Stellenbosch</long_name> 
      <short_name>Stellenbosch</short_name> 
      <type>locality</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>Stellenbosch</long_name> 
      <short_name>Stellenbosch</short_name> 
      <type>administrative_area_level_3</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>Brede River DC</long_name> 
      <short_name>Brede River DC</short_name> 
      <type>administrative_area_level_2</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>Western Cape</long_name> 
      <short_name>WC</short_name> 
      <type>administrative_area_level_1</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>South Africa</long_name> 
      <short_name>ZA</short_name> 
      <type>country</type> 
      <type>political</type> 
     </address_component> 
     <geometry> 
      <location> 
      <lat>-33.9354048</lat> 
      <lng>18.8640607</lng> 
      </location> 
      <location_type>APPROXIMATE</location_type> 
      <viewport> 
      <southwest> 
       <lat>-33.9437180</lat> 
       <lng>18.8449199</lng> 
      </southwest> 
      <northeast> 
       <lat>-33.9230960</lat> 
       <lng>18.8778929</lng> 
      </northeast> 
      </viewport> 
      <bounds> 
      <southwest> 
       <lat>-33.9437180</lat> 
       <lng>18.8449199</lng> 
      </southwest> 
      <northeast> 
       <lat>-33.9230960</lat> 
       <lng>18.8778929</lng> 
      </northeast> 
      </bounds> 
     </geometry> 
     </result> 
     <result> 
     <type>postal_code</type> 
     <formatted_address>7599, South Africa</formatted_address> 
     <address_component> 
      <long_name>7599</long_name> 
      <short_name>7599</short_name> 
      <type>postal_code</type> 
     </address_component> 
     <address_component> 
      <long_name>Brede River DC</long_name> 
      <short_name>Brede River DC</short_name> 
      <type>administrative_area_level_2</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>Western Cape</long_name> 
      <short_name>WC</short_name> 
      <type>administrative_area_level_1</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>South Africa</long_name> 
      <short_name>ZA</short_name> 
      <type>country</type> 
      <type>political</type> 
     </address_component> 
     <geometry> 
      <location> 
      <lat>-33.9300286</lat> 
      <lng>18.8640607</lng> 
      </location> 
      <location_type>APPROXIMATE</location_type> 
      <viewport> 
      <southwest> 
       <lat>-33.9693080</lat> 
       <lng>18.8019200</lng> 
      </southwest> 
      <northeast> 
       <lat>-33.8700550</lat> 
       <lng>18.9232900</lng> 
      </northeast> 
      </viewport> 
      <bounds> 
      <southwest> 
       <lat>-33.9693080</lat> 
       <lng>18.8019200</lng> 
      </southwest> 
      <northeast> 
       <lat>-33.8700550</lat> 
       <lng>18.9232900</lng> 
      </northeast> 
      </bounds> 
     </geometry> 
     </result> 
     <result> 
     <type>locality</type> 
     <type>political</type> 
     <formatted_address>Stellenbosch, South Africa</formatted_address> 
     <address_component> 
      <long_name>Stellenbosch</long_name> 
      <short_name>Stellenbosch</short_name> 
      <type>locality</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>Stellenbosch</long_name> 
      <short_name>Stellenbosch</short_name> 
      <type>administrative_area_level_3</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>Brede River DC</long_name> 
      <short_name>Brede River DC</short_name> 
      <type>administrative_area_level_2</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>Western Cape</long_name> 
      <short_name>WC</short_name> 
      <type>administrative_area_level_1</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>South Africa</long_name> 
      <short_name>ZA</short_name> 
      <type>country</type> 
      <type>political</type> 
     </address_component> 
     <geometry> 
      <location> 
      <lat>-33.9366667</lat> 
      <lng>18.8613889</lng> 
      </location> 
      <location_type>APPROXIMATE</location_type> 
      <viewport> 
      <southwest> 
       <lat>-34.0150869</lat> 
       <lng>18.7658819</lng> 
      </southwest> 
      <northeast> 
       <lat>-33.8782960</lat> 
       <lng>18.9232900</lng> 
      </northeast> 
      </viewport> 
      <bounds> 
      <southwest> 
       <lat>-34.0150869</lat> 
       <lng>18.7658819</lng> 
      </southwest> 
      <northeast> 
       <lat>-33.8782960</lat> 
       <lng>18.9232900</lng> 
      </northeast> 
      </bounds> 
     </geometry> 
     </result> 
     <result> 
     <type>postal_code</type> 
     <formatted_address>7600, South Africa</formatted_address> 
     <address_component> 
      <long_name>7600</long_name> 
      <short_name>7600</short_name> 
      <type>postal_code</type> 
     </address_component> 
     <address_component> 
      <long_name>Western Cape</long_name> 
      <short_name>WC</short_name> 
      <type>administrative_area_level_1</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>South Africa</long_name> 
      <short_name>ZA</short_name> 
      <type>country</type> 
      <type>political</type> 
     </address_component> 
     <geometry/> 
     </result> 
     <result> 
     <type>administrative_area_level_3</type> 
     <type>political</type> 
     <formatted_address>Stellenbosch, South Africa</formatted_address> 
     <address_component> 
      <long_name>Stellenbosch</long_name> 
      <short_name>Stellenbosch</short_name> 
      <type>administrative_area_level_3</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>Brede River DC</long_name> 
      <short_name>Brede River DC</short_name> 
      <type>administrative_area_level_2</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>Western Cape</long_name> 
      <short_name>WC</short_name> 
      <type>administrative_area_level_1</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>South Africa</long_name> 
      <short_name>ZA</short_name> 
      <type>country</type> 
      <type>political</type> 
     </address_component> 
     <geometry> 
      <location> 
      <lat>-33.9405478</lat> 
      <lng>18.9502232</lng> 
      </location> 
      <location_type>APPROXIMATE</location_type> 
      <viewport> 
      <southwest> 
       <lat>-34.0633899</lat> 
       <lng>18.7083300</lng> 
      </southwest> 
      <northeast> 
       <lat>-33.7933599</lat> 
       <lng>19.2438000</lng> 
      </northeast> 
      </viewport> 
      <bounds> 
      <southwest> 
       <lat>-34.0633899</lat> 
       <lng>18.7083300</lng> 
      </southwest> 
      <northeast> 
       <lat>-33.7933599</lat> 
       <lng>19.2438000</lng> 
      </northeast> 
      </bounds> 
     </geometry> 
     </result> 
     <result> 
     <type>administrative_area_level_2</type> 
     <type>political</type> 
     <formatted_address>Brede River DC, South Africa</formatted_address> 
     <address_component> 
      <long_name>Brede River DC</long_name> 
      <short_name>Brede River DC</short_name> 
      <type>administrative_area_level_2</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>Western Cape</long_name> 
      <short_name>WC</short_name> 
      <type>administrative_area_level_1</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>South Africa</long_name> 
      <short_name>ZA</short_name> 
      <type>country</type> 
      <type>political</type> 
     </address_component> 
     <geometry> 
      <location> 
      <lat>-33.4220698</lat> 
      <lng>19.7591675</lng> 
      </location> 
      <location_type>APPROXIMATE</location_type> 
      <viewport> 
      <southwest> 
       <lat>-34.1172599</lat> 
       <lng>18.7083299</lng> 
      </southwest> 
      <northeast> 
       <lat>-32.1844899</lat> 
       <lng>21.0103399</lng> 
      </northeast> 
      </viewport> 
      <bounds> 
      <southwest> 
       <lat>-34.1172599</lat> 
       <lng>18.7083299</lng> 
      </southwest> 
      <northeast> 
       <lat>-32.1844899</lat> 
       <lng>21.0103399</lng> 
      </northeast> 
      </bounds> 
     </geometry> 
     </result> 
     <result> 
     <type>administrative_area_level_1</type> 
     <type>political</type> 
     <formatted_address>Western Cape, South Africa</formatted_address> 
     <address_component> 
      <long_name>Western Cape</long_name> 
      <short_name>WC</short_name> 
      <type>administrative_area_level_1</type> 
      <type>political</type> 
     </address_component> 
     <address_component> 
      <long_name>South Africa</long_name> 
      <short_name>ZA</short_name> 
      <type>country</type> 
      <type>political</type> 
     </address_component> 
     <geometry> 
      <location> 
      <lat>-33.2277918</lat> 
      <lng>21.8568586</lng> 
      </location> 
      <location_type>APPROXIMATE</location_type> 
      <viewport> 
      <southwest> 
       <lat>-34.8330538</lat> 
       <lng>17.7575638</lng> 
      </southwest> 
      <northeast> 
       <lat>-30.4302599</lat> 
       <lng>24.2224100</lng> 
      </northeast> 
      </viewport> 
      <bounds> 
      <southwest> 
       <lat>-34.8330538</lat> 
       <lng>17.7575638</lng> 
      </southwest> 
      <northeast> 
       <lat>-30.4302599</lat> 
       <lng>24.2224100</lng> 
      </northeast> 
      </bounds> 
     </geometry> 
     </result> 
     <result> 
     <type>country</type> 
     <type>political</type> 
     <formatted_address>South Africa</formatted_address> 
     <address_component> 
      <long_name>South Africa</long_name> 
      <short_name>ZA</short_name> 
      <type>country</type> 
      <type>political</type> 
     </address_component> 
     <geometry> 
      <location> 
      <lat>-30.5594820</lat> 
      <lng>22.9375060</lng> 
      </location> 
      <location_type>APPROXIMATE</location_type> 
      <viewport> 
      <southwest> 
       <lat>-34.9670000</lat> 
       <lng>16.2817000</lng> 
      </southwest> 
      <northeast> 
       <lat>-22.1253869</lat> 
       <lng>33.0469000</lng> 
      </northeast> 
      </viewport> 
      <bounds> 
      <southwest> 
       <lat>-34.9670000</lat> 
       <lng>16.2817000</lng> 
      </southwest> 
      <northeast> 
       <lat>-22.1253869</lat> 
       <lng>33.0469000</lng> 
      </northeast> 
      </bounds> 
     </geometry> 
     </result> 
    </GeocodeResponse> 
+1

你能張貼address_component節點的示例XML?謝謝 –

+0

另請參見[在Scala中編組/解組XML](http://stackoverflow.com/questions/4664286/marshalling-unmarshalling-xml-in-scala) –

+0

在這種情況下,xml從他的Google地理編碼api中檢索,使用調度。 def lat lookupAddress(lat:Double,long:Double):Address = { val position = lat.toString +「,」+ long.toString val req = url(「http:// maps。 ) googleapis.com/maps/api/geocode/xml「)<<?地圖( 「經緯度」 - >位置, 「傳感器」 - > 「真」) HTTP(REQ { 節點=> buildAddress(節點) }) } – iandebeer

回答

1

除非你得到某種反思,否則你無法做得更好。看來你可以改善你如何搭配你的屬性,做這樣的事情,與它的純粹的苦差事幫助:

def buildAddress(geocodeResponse: NodeSeq) : Address = { 
    def getComponent(component: String): String = { 
     val longNames = for { 
      addressComponent <- geocodeResponse \\ "address_component" 
      componentType <- addressComponent \ "type" 
      if componentType.text == component 
     } yield addressComponent \ "long_name" 
     longNames.head.text 
    } 
    implicit def toC(s: String) = new { def c = getComponent(s) } 
    implicit val map = Map(
     'street -> ("route".c + "street_number".c), 
     'town -> "locality".c, 
     'suburb -> "sublocality".c, 
     'province -> "administrative_area_level_1".c, 
     'country -> "country".c, 
     'postalCode -> "postal_code".c 
    ) 
    Address('street, 'suburb, 'town, 'province, 'country, 'postalCode) 
} 
+0

感謝您的答覆 - 如果沒有改善實際的代碼,您的答案給予隱式函數的使用更好的處理。有趣的是,當你有實際用途時,抽象的東西突然變得清晰。我不得不包括進一步的過濾器,因爲google地理編碼api實際上可能會返回多個結果節點,正如您從我的問題中包含的示例xml中看到的那樣。 – iandebeer

+0

不錯,但它也說明了Scala的XML匹配的侷限性:(如果在那裏有更多的XPath會更好...... –

4

不是真的更多的功能,但匹配清除了一點東西。鑑於你必須尋找特定的字符串,並且處理值的規則取決於字符串,我不確定它有多「功能」。

def buildAddress(geocodeResponse: NodeSeq) : Address = { 
    val addressNodes = geocodeResponse \\ "address_component" 
    var street = " " 
    var town = "" 
    var suburb = "" 
    var province = "" 
    var country = "" 
    var postalCode = "" 

    addressNodes.foreach {node =>  
     lazy val text = (node \ "long_name").text // lazy so we don't look until we know it should be there 
    (node \ "type").head.text match { 
     case "street_number" => street = text + street 
     case "route"   => street = street + text 
     case "locality"  => town = text 
     case "sublocality" => suburb = text 
     case "administrative_area_level_1" => province = text 
     case "country"  => country = text 
     case "postal_code" => town = text 
     case _    => // Hmm. Not sure what is expected here. 

    } 
    } 
    Address(street,suburb,town,province,country,postalCode) 
} 

編輯:

我不知道它是如何更 「功能」 即可。

但話又說回來(注意 - 這又是未經檢驗的,我需要調試getComponent,但這個想法很明確,我希望更正以後可能會到達。)

def buildAddress(geocodeResponse: NodeSeq) : Address = { 
    val addressNodes = geocodeResponse \\ "address_component" 

    def getComponent(word:String) = { 
     addressNodes.find{_ \ "type".head.text == word) match { 
     case Some(node) => node \ "long_name".text 
     case _=> "" 
     } 
     } 

    Address(getComponent("street_number")+getComponent("route"), 
       getComponent("suburb"), 
       getComponent("town"), 
       getComponent("province"), 
       getComponent("country"), 
       getComponent("postalCode")) 

} 
+0

謝謝。爲了完整性,只需稍作修改:1)惰性val聲明必須在內部匹配中; 2)必須有一個處理被忽略的節點的默認情況。 – iandebeer

+0

感謝您的更正 –

+0

您的功能版本效率低得多,因爲您已經多次瀏覽序列。我認爲真正的問題是如何構建具有相同性能的功能解決方案。 – CheatEx

2

可以使用倍更加實用(並且僅遍歷序列一次)。我不確定我更喜歡你的代碼。不過這樣一來,你有沒有變種(一個新的地址,在每個步驟中創建)

addressNode.foldLeft(new Address("", "", "", "", "", "")){(a, node) => 
    lazy val text = (node \ "long_name").text 
    (node \ "type").head.text match { 
    case "street_number" => a.copy(street = a.street + text) 
    ... 
    case "locality" => a.copy(town = text) 
    ... 
    } 
} 
+0

這對我來說同時是醜陋和優雅的:)我喜歡在你訪問每個對象時建立完整的對象(雖然它確實很難知道Address是否真的完成),但是使用fold我)似乎不太清楚比理解... –

1

對於一個完全不同的方法,你可能會考慮XML pickler combinators,在GData Scala client可用。

def address: Pickler[Address] = 
    wrap(elem("address", 
      street ~ suburb ~ town ~ province ~ country ~ postalCode)) { 
     Address.apply 
    } { 
     (a: Address) => new ~(a.street, a.suburb) ~ a.town ~ a.province ~ a.country ~ a.postalCode 
    } 

def postalCode: Pickler[String] = elem("postal_code", text) 

// ... define picklers for street, suburb, town, province, country 

這不符合你的結構,但應該給味道。 pickler combinator庫也是available on bitbucket作爲單獨的庫。