2011-06-21 12 views
3

いくつかのxmlをケースクラスにマッピングしていますが、うまくいきますが、私の命令的なウインカーが優れた機能的ソリューションに盲目になっているような気がします。誰もがこれより良い方法を提案することができます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が派遣を使用して、彼は、Googleのジオコーディング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から取得されます、ディスパッチを使用します。 import dispatch._ def LookupAddress(double:double、long:Double):アドレス= { 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は実際に複数の結果ノードを返す可能性があるため、さらにフィルタを追加する必要がありました。 – 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) 
} 

EDIT:

私はそれがいかにより多くの "機能" わかりません。

しかし、その後、再び(注意 - これが再びテストされていない、私は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)レイジーヴァル宣言はインナーマッチの内側になければなりません。 2)無視されたノードを処理するデフォルトのケースが必要です。 – iandebeer

+0

訂正ありがとうございました –

+0

あなたの機能バージョンは、シーケンスを何度も歩き回るので、はるかに効率が悪いです。実際の問題は、同じパフォーマンスを持つ機能的なソリューションを構築する方法だと私は思います。 – CheatEx

2

あなたは折り目を使用することができますより機能的である(シーケンスを1回だけ歩く)。私はあなたのコードにそれを好むだろうか分からない。しかし、あなたは完全に異なるアプローチのためのVAR(新アドレスは各ステップで作成されます)

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

これは私の目に同時に醜いエレガントです:)私は完全なオブジェクトを構築するのが好きです(アドレスが本当に完全なときを知るのは難しいですが)私は)理解のためのものよりも明確ではないようです... –

1

を持っていないこの方法は、あなたがGData Scala clientで利用可能なXML pickler combinatorsを、考えるかもしれません。

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 

これはあなたの構造に一致しませんが、フレーバーを与える必要があります。ピックラーコンビネーターライブラリーは、別のライブラリーとしてavailable on bitbucketでもあります。

関連する問題