JSON APIで取得した文字列は、普通日本語部分がUnicodeコードポイントにエスケープされています。
たとえばTwitterAPIで取得したJSONは、単純に文字列として見ると以下のようになります。
# wget http://api.twitter.com/1/users/lookup.json?screen_name=TwitterJP
[{"name":"TwitterJP","time_zone":"Tokyo","follow_request_sent":null,"location":"\u6771\u4eac\u8d64\u5742","profile_image_url":"http:\/\/a0.twimg.com\/profile_images\/3407356865\/62f0d53222361fbd2c1fe9889f4cc559_normal.png","profile_sidebar_border_color":"FFFFFF","id":7080152,"followers_count":1211377,"status":{"possibly_sensitive":false,"in_reply_to_status_id":null,"truncated":false,"in_reply_to_status_id_str":null,"in_reply_to_screen_name":null,"geo":null,"source":"web","in_reply_to_user_id_str":null,"place":null,"favorited":false,"coordinates":null,"id":337745769207570433,"contributors":null,"in_reply_to_user_id":null,"id_str":"337745769207570433","retweet_count":54,"text":"1\u9031\u9593\u306e\u9593\u306b\u30ea\u30c4\u30a4\u30fc\u30c8\u304c\u591a\u304b\u3063\u305f\u30c4\u30a4\u30fc\u30c8\u3067\u3059\u3002\u30e9\u30c6\u30a2\u30fc\u30c8\u306e\u9032\u6b69\u306b\u3082\u9a5a\u304d\u307e\u3059\u304c\u3001\u3053\u3093\u306a\u3068\u3053\u308d\u3067\u30d2\u30fc\u30ed\u30fc\u306b\u51fa\u4f1a\u3063\u305f\u6642\u306e\u30b7\u30e7\u30c3\u30af\u306f\u2026\u3002http:\/\/t.co\/ycbubP2iY6","retweeted":false,"created_at":"Fri May 24 01:44:01 +0000 2013"},"profile_background_tile":true,"default_profile_image":false,"profile_sidebar_fill_color":"DDEEF6","is_translator":false,"profile_banner_url":"https:\/\/pbs.twimg.com\/profile_banners\/7080152\/1348004306","geo_enabled":true,"lang":"ja","favourites_count":5,"utc_offset":32400,"profile_image_url_https":"https:\/\/si0.twimg.com\/profile_images\/3407356865\/62f0d53222361fbd2c1fe9889f4cc559_normal.png","screen_name":"TwitterJP","profile_background_color":"C0DEED","created_at":"Tue Jun 26 01:54:35 +0000 2007","profile_background_image_url_https":"https:\/\/si0.twimg.com\/profile_background_images\/669680452\/4158abf21ac976cdb0459ccf14acd5f2.jpeg","protected":false,"description":"\u65e5\u672c\u8a9e\u7248Twitter\u516c\u5f0f\u30a2\u30ab\u30a6\u30f3\u30c8\u3067\u3059\u3002","profile_background_image_url":"http:\/\/a0.twimg.com\/profile_background_images\/669680452\/4158abf21ac976cdb0459ccf14acd5f2.jpeg","default_profile":false,"following":null,"verified":true,"profile_link_color":"0084B4","contributors_enabled":false,"friends_count":4307,"statuses_count":2143,"id_str":"7080152","profile_use_background_image":true,"profile_text_color":"333333","listed_count":16516,"url":"http:\/\/blog.jp.twitter.com","notifications":null}]
JSONライブラリで戻す
Rubyを使っているなら、特に難しいことを考えずにJSONに読み込ませればunescapeしてくれます。
require 'json' require 'open-uri' url = 'http://api.twitter.com/1/users/lookup.json?screen_name=TwitterJP' JSON.load(open(uri).read)
[{"name"=>"TwitterJP", "time_zone"=>"Tokyo", "follow_request_sent"=>false, "location"=>"東京赤坂", "profile_image_url"=>"http://a0.twimg.com/profile_images/3407356865/62f0d53222361fbd2c1fe9889f4cc559_normal.png", "profile_sidebar_border_color"=>"FFFFFF", "profile_image_url_https"=>"https://si0.twimg.com/profile_images/3407356865/62f0d53222361fbd2c1fe9889f4cc559_normal.png", "id"=>7080152, "followers_count"=>1211378, "status"=>{"possibly_sensitive"=>false, "in_reply_to_status_id"=>nil, "truncated"=>false, "in_reply_to_screen_name"=>nil, "geo"=>nil, "source"=>"web", "place"=>nil, "favorited"=>false, "coordinates"=>nil, "id"=>337745769207570433, "in_reply_to_status_id_str"=>nil, "contributors"=>nil, "in_reply_to_user_id"=>nil, "id_str"=>"337745769207570433", "retweet_count"=>54, "text"=>"1週間の間にリツイートが多かったツイートです。ラテアートの進歩にも驚きますが、こんなところでヒーローに出会った時のショックは…。http://t.co/ycbubP2iY6", "in_reply_to_user_id_str"=>nil, "retweeted"=>false, "created_at"=>"Fri May 24 01:44:01 +0000 2013"}, "profile_background_tile"=>true, "default_profile_image"=>false, "profile_sidebar_fill_color"=>"DDEEF6", "profile_banner_url"=>"https://pbs.twimg.com/profile_banners/7080152/1348004306", "geo_enabled"=>true, "lang"=>"ja", "favourites_count"=>5, "utc_offset"=>32400, "screen_name"=>"TwitterJP", "profile_background_color"=>"C0DEED", "created_at"=>"Tue Jun 26 01:54:35 +0000 2007", "profile_background_image_url_https"=>"https://si0.twimg.com/profile_background_images/669680452/4158abf21ac976cdb0459ccf14acd5f2.jpeg", "protected"=>false, "description"=>"日本語版Twitter公式 カウントです。", "profile_background_image_url"=>"http://a0.twimg.com/profile_background_images/669680452/4158abf21ac976cdb0459ccf14acd5f2.jpeg", "default_profile"=>false, "following"=>false, "verified"=>true, "profile_link_color"=>"0084B4", "contributors_enabled"=>false, "friends_count"=>4307, "statuses_count"=>2143, "is_translator"=>false, "id_str"=>"7080152", "profile_use_background_image"=>true, "profile_text_color"=>"333333", "listed_count"=>16516, "url"=>"http://blog.jp.twitter.com", "notifications"=>false}]
手動で戻す
しかし、何らかの事情で、通常ファイルやデータベースにこのUnicodeエスケープシーケンスが含まれていることもあるでしょう。
そんなときは、packを使って手動で処理してやります。
str = 'Twitter\u516c\u5f0f\u30a2\u30ab\u30a6\u30f3\u30c8\u3067\u3059\u3002' puts str.gsub(/\\u([\da-fA-F]{4})/) { [$1].pack('H*').unpack('n*').pack('U*') } # => 日本語版Twitter公式アカウントです。
文字参照(16進表記)を解決する
HTMLなどで、文字参照の形式になっていることがあります。
Twitter公式アカウントです。
HTMLのほか、PDFから文字列を抽出するライブラリで、文字列の途中から急に文字参照になっていることもありました。
これも、先ほどと全く同じ方法で戻せます。
str.gsub(/&#x([\da-fA-F]+);/) { [$1].pack('H*').unpack('n*').pack('U') }
文字参照(10進表記)を解決する
文字参照は、10進表記もよく使われます。
Twitter公式アカウントです。
この場合も似たような感じで処理してやりましょう。
str.gsub(/&#(\d+);/) { [$1.to_i].pack('U') }
文字参照に関してはhtmlentitiesを使う方が簡単ですが、やっていることは似たような感じです。