Сценарий PHP не может анализировать JSON, возвращаемый API-интерфейсом Google Maps Geocoder, если геокодер возвращает несколько результатов.

У меня есть PHP-скрипт, который пытается читать и анализировать строки JSON, возвращаемые из Google Geocoder API. Сценарий отлично работает для большинства адресов, но в некоторых случаях возникают проблемы, когда Google Geocoder API возвращает несколько результатов для данного адреса.

Это мой код:

$mGeoFile = fopen("http://maps.googleapis.com/maps/api/geocode/json?address=" . str_replace(" ", "+", $mAddress0) . "&sensor=false", "r");       
$mGeoFileJSON = fread($mGeoFile, 10000);
fclose($mGeoFile);
$mGeocode_array = json_decode($mGeoFileJSON, true);

var_dump($mGeocode_array);
echo "<br />";
var_dump($mGeocode_array['results'][0]['geometry']['location']);

Который печатает это для "115 Chestnut Street Upton, MA" (передается в $mAddress0):

array(2) { ["status"]=> string(2) "OK" ["results"]=> array(1) { [0]=> array(4) { ["types"]=> array(1) { [0]=> string(14) "street_address" } ["formatted_address"]=> string(37) "115 Chestnut St, Upton, MA 01568, USA" ["address_components"]=> array(8) { [0]=> array(3) { ["long_name"]=> string(3) "115" ["short_name"]=> string(3) "115" ["types"]=> array(1) { [0]=> string(13) "street_number" } } [1]=> array(3) { ["long_name"]=> string(11) "Chestnut St" ["short_name"]=> string(11) "Chestnut St" ["types"]=> array(1) { [0]=> string(5) "route" } } [2]=> array(3) { ["long_name"]=> string(5) "Upton" ["short_name"]=> string(5) "Upton" ["types"]=> array(2) { [0]=> string(8) "locality" [1]=> string(9) "political" } } [3]=> array(3) { ["long_name"]=> string(5) "Upton" ["short_name"]=> string(5) "Upton" ["types"]=> array(2) { [0]=> string(27) "administrative_area_level_3" [1]=> string(9) "political" } } [4]=> array(3) { ["long_name"]=> string(9) "Worcester" ["short_name"]=> string(9) "Worcester" ["types"]=> array(2) { [0]=> string(27) "administrative_area_level_2" [1]=> string(9) "political" } } [5]=> array(3) { ["long_name"]=> string(13) "Massachusetts" ["short_name"]=> string(2) "MA" ["types"]=> array(2) { [0]=> string(27) "administrative_area_level_1" [1]=> string(9) "political" } } [6]=> array(3) { ["long_name"]=> string(13) "United States" ["short_name"]=> string(2) "US" ["types"]=> array(2) { [0]=> string(7) "country" [1]=> string(9) "political" } } [7]=> array(3) { ["long_name"]=> string(5) "01568" ["short_name"]=> string(5) "01568" ["types"]=> array(1) { [0]=> string(11) "postal_code" } } } ["geometry"]=> array(4) { ["location"]=> array(2) { ["lat"]=> float(42.1476896) ["lng"]=> float(-71.5863099) } ["location_type"]=> string(18) "RANGE_INTERPOLATED" ["viewport"]=> array(2) { ["southwest"]=> array(2) { ["lat"]=> float(42.1445379) ["lng"]=> float(-71.5894495) } ["northeast"]=> array(2) { ["lat"]=> float(42.1508332) ["lng"]=> float(-71.5831542) } } ["bounds"]=> array(2) { ["southwest"]=> array(2) { ["lat"]=> float(42.1476815) ["lng"]=> float(-71.5863099) } ["northeast"]=> array(2) { ["lat"]=> float(42.1476896) ["lng"]=> float(-71.5862938) } } } } } } 
array(2) { ["lat"]=> float(42.1476896) ["lng"]=> float(-71.5863099) }

Также геокодер возвращает следующую строку JSON для «115 Chestnut Street Upton, MA».

{
  "status": "OK",
  "results": [ {
    "types": [ "street_address" ],
    "formatted_address": "115 Chestnut St, Upton, MA 01568, USA",
    "address_components": [ {
      "long_name": "115",
      "short_name": "115",
      "types": [ "street_number" ]
    }, {
      "long_name": "Chestnut St",
      "short_name": "Chestnut St",
      "types": [ "route" ]
    }, {
      "long_name": "Upton",
      "short_name": "Upton",
      "types": [ "locality", "political" ]
    }, {
      "long_name": "Upton",
      "short_name": "Upton",
      "types": [ "administrative_area_level_3", "political" ]
    }, {
      "long_name": "Worcester",
      "short_name": "Worcester",
      "types": [ "administrative_area_level_2", "political" ]
    }, {
      "long_name": "Massachusetts",
      "short_name": "MA",
      "types": [ "administrative_area_level_1", "political" ]
    }, {
      "long_name": "United States",
      "short_name": "US",
      "types": [ "country", "political" ]
    }, {
      "long_name": "01568",
      "short_name": "01568",
      "types": [ "postal_code" ]
    } ],
    "geometry": {
      "location": {
        "lat": 42.1476896,
        "lng": -71.5863099
      },
      "location_type": "RANGE_INTERPOLATED",
      "viewport": {
        "southwest": {
          "lat": 42.1445379,
          "lng": -71.5894495
        },
        "northeast": {
          "lat": 42.1508332,
          "lng": -71.5831542
        }
      },
      "bounds": {
        "southwest": {
          "lat": 42.1476815,
          "lng": -71.5863099
        },
        "northeast": {
          "lat": 42.1476896,
          "lng": -71.5862938
        }
      }
    }
  } ]
}

У меня возникла проблема с таким адресом, как 50 Franklin Street Boston, MA, для которого Google Geocoder API возвращает:

{
  "status": "OK",
  "results": [ {
    "types": [ "street_address" ],
    "formatted_address": "50 Franklin St, Boston, MA 02110, USA",
    "address_components": [ {
      "long_name": "50",
      "short_name": "50",
      "types": [ "street_number" ]
    }, {
      "long_name": "Franklin St",
      "short_name": "Franklin St",
      "types": [ "route" ]
    }, {
      "long_name": "Boston",
      "short_name": "Boston",
      "types": [ "locality", "political" ]
    }, {
      "long_name": "Boston",
      "short_name": "Boston",
      "types": [ "administrative_area_level_3", "political" ]
    }, {
      "long_name": "Suffolk",
      "short_name": "Suffolk",
      "types": [ "administrative_area_level_2", "political" ]
    }, {
      "long_name": "Massachusetts",
      "short_name": "MA",
      "types": [ "administrative_area_level_1", "political" ]
    }, {
      "long_name": "United States",
      "short_name": "US",
      "types": [ "country", "political" ]
    }, {
      "long_name": "02110",
      "short_name": "02110",
      "types": [ "postal_code" ]
    } ],
    "geometry": {
      "location": {
        "lat": 42.3556540,
        "lng": -71.0585261
      },
      "location_type": "ROOFTOP",
      "viewport": {
        "southwest": {
          "lat": 42.3525064,
          "lng": -71.0616737
        },
        "northeast": {
          "lat": 42.3588016,
          "lng": -71.0553785
        }
      }
    }
  }, {
    "types": [ "street_address" ],
    "formatted_address": "50 Franklin St, Boston, MA 02122, USA",
    "address_components": [ {
      "long_name": "50",
      "short_name": "50",
      "types": [ "street_number" ]
    }, {
      "long_name": "Franklin St",
      "short_name": "Franklin St",
      "types": [ "route" ]
    }, {
      "long_name": "Boston",
      "short_name": "Boston",
      "types": [ "locality", "political" ]
    }, {
      "long_name": "Boston",
      "short_name": "Boston",
      "types": [ "administrative_area_level_3", "political" ]
    }, {
      "long_name": "Suffolk",
      "short_name": "Suffolk",
      "types": [ "administrative_area_level_2", "political" ]
    }, {
      "long_name": "Massachusetts",
      "short_name": "MA",
      "types": [ "administrative_area_level_1", "political" ]
    }, {
      "long_name": "United States",
      "short_name": "US",
      "types": [ "country", "political" ]
    }, {
      "long_name": "02122",
      "short_name": "02122",
      "types": [ "postal_code" ]
    } ],
    "geometry": {
      "location": {
        "lat": 42.2871370,
        "lng": -71.0400105
      },
      "location_type": "ROOFTOP",
      "viewport": {
        "southwest": {
          "lat": 42.2839894,
          "lng": -71.0431581
        },
        "northeast": {
          "lat": 42.2902846,
          "lng": -71.0368629
        }
      }
    }
  }, {
    "types": [ "street_address" ],
    "formatted_address": "50 Franklin St, Boston, MA 02129, USA",
    "address_components": [ {
      "long_name": "50",
      "short_name": "50",
      "types": [ "street_number" ]
    }, {
      "long_name": "Franklin St",
      "short_name": "Franklin St",
      "types": [ "route" ]
    }, {
      "long_name": "Boston",
      "short_name": "Boston",
      "types": [ "locality", "political" ]
    }, {
      "long_name": "Boston",
      "short_name": "Boston",
      "types": [ "administrative_area_level_3", "political" ]
    }, {
      "long_name": "Suffolk",
      "short_name": "Suffolk",
      "types": [ "administrative_area_level_2", "political" ]
    }, {
      "long_name": "Massachusetts",
      "short_name": "MA",
      "types": [ "administrative_area_level_1", "political" ]
    }, {
      "long_name": "United States",
      "short_name": "US",
      "types": [ "country", "political" ]
    }, {
      "long_name": "02129",
      "short_name": "02129",
      "types": [ "postal_code" ]
    } ],
    "geometry": {
      "location": {
        "lat": 42.3785674,
        "lng": -71.0668132
      },
      "location_type": "RANGE_INTERPOLATED",
      "viewport": {
        "southwest": {
          "lat": 42.3754150,
          "lng": -71.0699535
        },
        "northeast": {
          "lat": 42.3817103,
          "lng": -71.0636583
        }
      },
      "bounds": {
        "southwest": {
          "lat": 42.3785579,
          "lng": -71.0668132
        },
        "northeast": {
          "lat": 42.3785674,
          "lng": -71.0667986
        }
      }
    }
  }, {
    "types": [ "street_address" ],
    "formatted_address": "50 Franklin St, Boston, MA 02136, USA",
    "address_components": [ {
      "long_name": "50",
      "short_name": "50",
      "types": [ "street_number" ]
    }, {
      "long_name": "Franklin St",
      "short_name": "Franklin St",
      "types": [ "route" ]
    }, {
      "long_name": "Boston",
      "short_name": "Boston",
      "types": [ "locality", "political" ]
    }, {
      "long_name": "Boston",
      "short_name": "Boston",
      "types": [ "administrative_area_level_3", "political" ]
    }, {
      "long_name": "Suffolk",
      "short_name": "Suffolk",
      "types": [ "administrative_area_level_2", "political" ]
    }, {
      "long_name": "Massachusetts",
      "short_name": "MA",
      "types": [ "administrative_area_level_1", "political" ]
    }, {
      "long_name": "United States",
      "short_name": "US",
      "types": [ "country", "political" ]
    }, {
      "long_name": "02136",
      "short_name": "02136",
      "types": [ "postal_code" ]
    } ],
    "geometry": {
      "location": {
        "lat": 42.2526044,
        "lng": -71.1332635
      },
      "location_type": "RANGE_INTERPOLATED",
      "viewport": {
        "southwest": {
          "lat": 42.2494625,
          "lng": -71.1364052
        },
        "northeast": {
          "lat": 42.2557577,
          "lng": -71.1301100
        }
      },
      "bounds": {
        "southwest": {
          "lat": 42.2526044,
          "lng": -71.1332635
        },
        "northeast": {
          "lat": 42.2526158,
          "lng": -71.1332517
        }
      }
    },
    "partial_match": true
  } ]
}

И мой скрипт возвращает:

NULL 
NULL

Любые идеи, что может происходить? Спасибо!


person Casey Flynn    schedule 02.02.2011    source источник


Ответы (2)


Вполне возможно, что ответ содержит более 10 тыс. символов, в этом случае вы пытаетесь декодировать недопустимую строку JSON. Перед декодированием убедитесь, что вы читаете весь поток. Самый простой способ — использовать file_get_contents вместо fopen/fread/fclose. В долгосрочной перспективе вы должны переключиться на керлинг.

person Maerlyn    schedule 02.02.2011
comment
Спасибо @Maerlyn, я на самом деле установил размер файла около 1 000 000, потому что не думал, что он может быть больше этого. Но это предложение работает. :) - person Casey Flynn; 02.02.2011
comment
Это лечит только симптом, а не его причину. Это хорошо только на короткий срок, и это вернется к вам, когда вы перейдете на сервер с отключенным allow_url_fopen. - person Maerlyn; 02.02.2011

Для меня ваша строка отлично декодируется. Возможно, ответ кодируется в UTF-8 и в строке присутствует символ BOM (знак порядка байтов). В этом случае строка не является правильно отформатированной структурой JSON (см. RFC 4627).

На его месте я бы написал еще несколько строк, чтобы понять, не в этом ли проблема. Вы можете удалить символы B.O.M, упаковывающие CCC в бинарную строку. Вызовите эту функцию рекурсивно:

function removeBOM($str=""){
        if(substr($str, 0,3) == pack("CCC",0xef,0xbb,0xbf)) {
                $str=substr($str, 3);
        }
        return $str;
}
person Gustavo Costa De Oliveira    schedule 02.02.2011