Более функциональный подход к анализу XML - PullRequest
4 голосов
/ 21 июня 2011

Я сопоставляю некоторый xml с классом case, и он работает нормально, но у меня есть ощущение, что мои императивные жалюзи ослепляют меня к лучшему функциональному решению.Кто-нибудь может предложить лучший способ, чем этот:

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 геокодирования Google с использованием Dispatch.

   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>

Ответы [ 4 ]

4 голосов
/ 21 июня 2011

Не совсем более функционально, но совпадение немного проясняет ситуацию.Учитывая, что вам нужно искать конкретные строки, а правила работы со значением зависят от строки, я не уверен, насколько «функциональным» оно может быть.

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"))

}
2 голосов
/ 21 июня 2011

Если вы не получили какое-то отражение, вы действительно мало что можете сделать, чтобы сделать его лучше.Кажется, вы могли бы улучшить соответствие своих атрибутов, выполнив что-то вроде этого, чтобы помочь с рутинной работой:

2 голосов
/ 21 июня 2011

Вы можете использовать сгиб, чтобы быть более функциональным (и пройти последовательность только один раз). Я не уверен, что предпочел бы это вашему коду. Но таким образом у вас нет 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)
    ...
   }
}
1 голос
/ 21 июня 2011

Для совершенно другого подхода вы можете рассмотреть XML-комбинаторы выбора , доступные в клиенте GData Scala .

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

Это не соответствует вашей структуре, но должно придать вкус. Библиотека комбинатора выбора также доступна в bitbucket как отдельная библиотека.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...