こんにちは。ねこまたです。PytnonでJSONをパースできるようにしました。space、string、array、null、boolean、objectのパースを実装したので、以下にソースコードの一部とともに実装の際考えたことなどを書いていきます。
def is_digit(c):
# 文字列の大小比較
return "0" <= c and c <= "9"
大小比較の表現を用いてJSONが数字であるかを調べます。
class Parser:
def __init__(self, s) -> None:
self.s = s
self.pos = 0
self.s
は読み込む文字列、self.pos
は現在の位置を示します。これを用いて後に行うパースで文字を一つ進める処理を行います。
def peek(self):
if self.pos < len(self.s):
return self.s[self.pos]
else:
raise ParseError()
self.pos
が読み込む文字列の長さより小さいとき、読み込む文字列のself.pos
番目(=現在の位置)を返す関数を定義します。
def spaces(self):
while True:
try:
c = self.peek()
except ParseError:
break
if c == " " or c == "\n" or c == "\t" or c == "\r":
self.pos += 1
else:
break
スペースを無視するようにします。
2. stringのパース
string
をパースするためには、文字であるchar
のパースを行い、string
関数の中でchar
関数を呼び出す必要があります。
def char(self):
c = self.peek()
if c == "\"":
raise ParseError()
if c == "\\":
self.pos += 1
c = self.peek()
if c == "n":
self.pos += 1
return "\n"
if c == "r":
self.pos += 1
return "\r"
if c == "t":
self.pos += 1
return "\t"
if c == "\\":
self.pos += 1
return "\\"
if c == "\"":
self.pos += 1
return "\""
raise ParseError()
else:
self.pos += 1
return c
ここでは文字列が"
のときエラーを返し、\n
、\r
、\t
、\\
、\"
が出てきた場合はそれぞれ中身を変えずに返し、その他のエスケープシーケンス
が出てきた場合にもエラーを返しています。
def string(self):
res = ""
c = self.peek()
if not c == "\"":
raise ParseError()
self.pos += 1
while True:
c = self.peek()
if c == "\"":
self.pos += 1
return res
x = self.char()
res += x
まず self.peek
関数を呼び出して文字列の先頭の文字を取得し、"
から始まっていなければエラーを返します。"
から始まっていればJSONの文字列であることから、self.pos
を1進めてパースを続けます。その後、char
関数を呼び出し、文字列の末尾が"になるまでパースします。文字列の中でパースを終えた部分の文字をres
に追加していき、最後にres
を返します。これで文字列のパースが完了します。
3. arrayのパース
array
をパースするためには、,
で区切られた配列の要素を先頭から順に調べる必要があります。
def array(self):
res = []
c = self.peek()
if c != "[":
raise ParseError()
self.pos += 1
self.spaces()
c = self.peek()
if c == "]":
self.pos += 1
return res
x = self.json()
res.append(x)
while True:
self.spaces()
c = self.peek()
if c == "]":
self.pos += 1
return res
if c != ",":
raise ParseError()
self.pos += 1
self.spaces()
x = self.json()
res.append(x)
まず self.peek
関数を呼び出して文字列の先頭の文字を取得し、[
から始まっていなければエラーを返します。self.pos
を一つ進め、spaces
関数を呼び出し、配列の中に含まれるスペースを無視します。次に、もう一度self.peek
関数を呼び出し、配列の末尾が]
になっていればres
を返して終了します。末尾が]
でなければjson
関数を呼び出し、res
にx
を追加します。次に、再びスペースを無視し、配列の先頭を取得します。while
の中では、配列の末尾が]
になるまでパースを続けます。配列の先頭[
を除いた先頭の文字の次の文字が,
でなければ エラーを返し、]
が再び現れるまでパースを続けます。
def json(self):
c = self.peek()
if c == "[":
return self.array()
if c == "t" or c == "f":
return self.boolean()
if c == "n":
return self.null()
if c == "\"":
return self.string()
if c == "{":
return self.object()
return self.parse_natural()
↑json
関数
正常に動いているか調べるため、自分のTwitterのユーザー情報を取得し、パースを行いました。
入力
{"id":1346478670240616400,"id_str":"1346478670240616449","name":"ねこまた","screen_name":"what_is_pyoni","location":"","profile_location":null,"description":"https://t.co/9roPByIsvY Okayama Univ.🌏 B1 / OUCRC","url":null,"entities":{"description":{"urls":[{"url":"https://t.co/9roPByIsvY","expanded_url":"http://kyon.earth","display_url":"kyon.earth","indices":[0,23]}]}},"protected":false,"followers_count":92,"friends_count":112,"listed_count":0,"created_at":"Tue Jan 05 15:28:40 +0000 2021","favourites_count":11013,"utc_offset":null,"time_zone":null,"geo_enabled":true,"verified":false,"statuses_count":1340,"lang":null,"status":{"created_at":"Sun May 01 06:18:35 +0000 2022","id":1520648861420687400,"id_str":"1520648861420687360","text":"RT @Takano_Shiho: 夏の宝石 https://t.co/zSDV208rAG","truncated":false,"extended_entities":{"media":[{"id":1520564805353885700,"id_str":"1520564805353885696","indices":[23,46],"media_url":"http://pbs.twimg.com/media/FRoholkUUAA3rbM.jpg","media_url_https":"https://pbs.twimg.com/media/FRoholkUUAA3rbM.jpg","url":"https://t.co/zSDV208rAG","display_url":"pic.twitter.com/zSDV208rAG","expanded_url":"https://twitter.com/Takano_Shiho/status/1520564817349677057/photo/1","type":"photo","sizes":{"thumb":{"w":150,"h":150,"resize":"crop"},"small":{"w":680,"h":383,"resize":"fit"},"medium":{"w":1200,"h":675,"resize":"fit"},"large":{"w":2048,"h":1152,"resize":"fit"}},"source_status_id":1520564817349677000,"source_status_id_str":"1520564817349677057","source_user_id":793854134,"source_user_id_str":"793854134"},{"id":1520564805475602400,"id_str":"1520564805475602434","indices":[23,46],"media_url":"http://pbs.twimg.com/media/FRohomBVkAIzaq-.jpg","media_url_https":"https://pbs.twimg.com/media/FRohomBVkAIzaq-.jpg","url":"https://t.co/zSDV208rAG","display_url":"pic.twitter.com/zSDV208rAG","expanded_url":"https://twitter.com/Takano_Shiho/status/1520564817349677057/photo/1","type":"photo","sizes":{"large":{"w":2048,"h":1152,"resize":"fit"},"medium":{"w":1200,"h":675,"resize":"fit"},"thumb":{"w":150,"h":150,"resize":"crop"},"small":{"w":680,"h":383,"resize":"fit"}},"source_status_id":1520564817349677000,"source_status_id_str":"1520564817349677057","source_user_id":793854134,"source_user_id_str":"793854134"},{"id":1520564805534290000,"id_str":"1520564805534289920","indices":[23,46],"media_url":"http://pbs.twimg.com/media/FRohomPVEAASBwR.jpg","media_url_https":"https://pbs.twimg.com/media/FRohomPVEAASBwR.jpg","url":"https://t.co/zSDV208rAG","display_url":"pic.twitter.com/zSDV208rAG","expanded_url":"https://twitter.com/Takano_Shiho/status/1520564817349677057/photo/1","type":"photo","sizes":{"thumb":{"w":150,"h":150,"resize":"crop"},"small":{"w":680,"h":383,"resize":"fit"},"medium":{"w":1200,"h":675,"resize":"fit"},"large":{"w":2048,"h":1152,"resize":"fit"}},"source_status_id":1520564817349677000,"source_status_id_str":"1520564817349677057","source_user_id":793854134,"source_user_id_str":"793854134"},{"id":1520564805609791500,"id_str":"1520564805609791488","indices":[23,46],"media_url":"http://pbs.twimg.com/media/FRohomhVIAAQSrm.jpg","media_url_https":"https://pbs.twimg.com/media/FRohomhVIAAQSrm.jpg","url":"https://t.co/zSDV208rAG","display_url":"pic.twitter.com/zSDV208rAG","expanded_url":"https://twitter.com/Takano_Shiho/status/1520564817349677057/photo/1","type":"photo","sizes":{"medium":{"w":1200,"h":675,"resize":"fit"},"large":{"w":2048,"h":1152,"resize":"fit"},"thumb":{"w":150,"h":150,"resize":"crop"},"small":{"w":680,"h":383,"resize":"fit"}},"source_status_id":1520564817349677000,"source_status_id_str":"1520564817349677057","source_user_id":793854134,"source_user_id_str":"793854134"}]},"source":"<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>","in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"geo":null,"coordinates":null,"place":null,"contributors":null,"retweeted_status":{"created_at":"Sun May 01 00:44:37 +0000 2022","id":1520564817349677000,"id_str":"1520564817349677057","text":"夏の宝石 https://t.co/zSDV208rAG","truncated":false,"extended_entities":{"media":[{"id":1520564805353885700,"id_str":"1520564805353885696","indices":[5,28],"media_url":"http://pbs.twimg.com/media/FRoholkUUAA3rbM.jpg","media_url_https":"https://pbs.twimg.com/media/FRoholkUUAA3rbM.jpg","url":"https://t.co/zSDV208rAG","display_url":"pic.twitter.com/zSDV208rAG","expanded_url":"https://twitter.com/Takano_Shiho/status/1520564817349677057/photo/1","type":"photo","sizes":{"thumb":{"w":150,"h":150,"resize":"crop"},"small":{"w":680,"h":383,"resize":"fit"},"medium":{"w":1200,"h":675,"resize":"fit"},"large":{"w":2048,"h":1152,"resize":"fit"}}},{"id":1520564805475602400,"id_str":"1520564805475602434","indices":[5,28],"media_url":"http://pbs.twimg.com/media/FRohomBVkAIzaq-.jpg","media_url_https":"https://pbs.twimg.com/media/FRohomBVkAIzaq-.jpg","url":"https://t.co/zSDV208rAG","display_url":"pic.twitter.com/zSDV208rAG","expanded_url":"https://twitter.com/Takano_Shiho/status/1520564817349677057/photo/1","type":"photo","sizes":{"large":{"w":2048,"h":1152,"resize":"fit"},"medium":{"w":1200,"h":675,"resize":"fit"},"thumb":{"w":150,"h":150,"resize":"crop"},"small":{"w":680,"h":383,"resize":"fit"}}},{"id":1520564805534290000,"id_str":"1520564805534289920","indices":[5,28],"media_url":"http://pbs.twimg.com/media/FRohomPVEAASBwR.jpg","media_url_https":"https://pbs.twimg.com/media/FRohomPVEAASBwR.jpg","url":"https://t.co/zSDV208rAG","display_url":"pic.twitter.com/zSDV208rAG","expanded_url":"https://twitter.com/Takano_Shiho/status/1520564817349677057/photo/1","type":"photo","sizes":{"thumb":{"w":150,"h":150,"resize":"crop"},"small":{"w":680,"h":383,"resize":"fit"},"medium":{"w":1200,"h":675,"resize":"fit"},"large":{"w":2048,"h":1152,"resize":"fit"}}},{"id":1520564805609791500,"id_str":"1520564805609791488","indices":[5,28],"media_url":"http://pbs.twimg.com/media/FRohomhVIAAQSrm.jpg","media_url_https":"https://pbs.twimg.com/media/FRohomhVIAAQSrm.jpg","url":"https://t.co/zSDV208rAG","display_url":"pic.twitter.com/zSDV208rAG","expanded_url":"https://twitter.com/Takano_Shiho/status/1520564817349677057/photo/1","type":"photo","sizes":{"medium":{"w":1200,"h":675,"resize":"fit"},"large":{"w":2048,"h":1152,"resize":"fit"},"thumb":{"w":150,"h":150,"resize":"crop"},"small":{"w":680,"h":383,"resize":"fit"}}}]},"source":"<a href=\"http://twitter.com/download/iphone\" rel=\"nofollow\">Twitter for iPhone</a>","in_reply_to_status_id":null,"in_reply_to_status_id_str":null,"in_reply_to_user_id":null,"in_reply_to_user_id_str":null,"in_reply_to_screen_name":null,"geo":null,"coordinates":null,"place":null,"contributors":null,"is_quote_status":false,"retweet_count":76,"favorite_count":589,"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"ja"},"is_quote_status":false,"retweet_count":76,"favorite_count":0,"favorited":false,"retweeted":false,"possibly_sensitive":false,"lang":"ja"},"contributors_enabled":false,"is_translator":false,"is_translation_enabled":false,"profile_background_color":"F5F8FA","profile_background_image_url":null,"profile_background_image_url_https":null,"profile_background_tile":false,"profile_image_url":"http://pbs.twimg.com/profile_images/1511726514634588166/0L3xPaQ4_normal.jpg","profile_image_url_https":"https://pbs.twimg.com/profile_images/1511726514634588166/0L3xPaQ4_normal.jpg","profile_banner_url":"https://pbs.twimg.com/profile_banners/1346478670240616449/1650041603","profile_link_color":"1DA1F2","profile_sidebar_border_color":"C0DEED","profile_sidebar_fill_color":"DDEEF6","profile_text_color":"333333","profile_use_background_image":true,"has_extended_profile":true,"default_profile":true,"default_profile_image":false,"following":null,"follow_request_sent":null,"notifications":null,"translator_type":"none","withheld_in_countries":[]}
出力
{'id': 1346478670240616400, 'id_str': '1346478670240616449', 'name': 'ねこまた', 'screen_name': 'what_is_pyoni', 'location': '', 'profile_location': None, 'description': 'https://t.co/9roPByIsvY Okayama Univ.🌏 B1 / OUCRC', 'url': None, 'entities': {'description': {'urls': [{'url': 'https://t.co/9roPByIsvY', 'expanded_url': 'http://kyon.earth', 'display_url': 'kyon.earth', 'indices': [0, 23]}]}}, 'protected': False, 'followers_count': 92, 'friends_count': 112, 'listed_count': 0, 'created_at': 'Tue Jan 05 15:28:40 +0000 2021', 'favourites_count': 11013, 'utc_offset': None, 'time_zone': None, 'geo_enabled': True, 'verified': False, 'statuses_count': 1340, 'lang': None, 'status': {'created_at': 'Sun May 01 06:18:35 +0000 2022', 'id': 1520648861420687400, 'id_str': '1520648861420687360', 'text': 'RT @Takano_Shiho: 夏の宝石 https://t.co/zSDV208rAG', 'truncated': False, 'extended_entities': {'media': [{'id': 1520564805353885700, 'id_str': '1520564805353885696', 'indices': [23, 46], 'media_url': 'http://pbs.twimg.com/media/FRoholkUUAA3rbM.jpg', 'media_url_https': 'https://pbs.twimg.com/media/FRoholkUUAA3rbM.jpg', 'url': 'https://t.co/zSDV208rAG', 'display_url': 'pic.twitter.com/zSDV208rAG', 'expanded_url': 'https://twitter.com/Takano_Shiho/status/1520564817349677057/photo/1', 'type': 'photo', 'sizes': {'thumb': {'w': 150, 'h': 150, 'resize': 'crop'}, 'small': {'w': 680, 'h': 383, 'resize': 'fit'}, 'medium': {'w': 1200, 'h': 675, 'resize': 'fit'}, 'large': {'w': 2048, 'h': 1152, 'resize': 'fit'}}, 'source_status_id': 1520564817349677000, 'source_status_id_str': '1520564817349677057', 'source_user_id': 793854134, 'source_user_id_str': '793854134'}, {'id': 1520564805475602400, 'id_str': '1520564805475602434', 'indices': [23, 46], 'media_url': 'http://pbs.twimg.com/media/FRohomBVkAIzaq-.jpg', 'media_url_https': 'https://pbs.twimg.com/media/FRohomBVkAIzaq-.jpg', 'url': 'https://t.co/zSDV208rAG', 'display_url': 'pic.twitter.com/zSDV208rAG', 'expanded_url': 'https://twitter.com/Takano_Shiho/status/1520564817349677057/photo/1', 'type': 'photo', 'sizes': {'large': {'w': 2048, 'h': 1152, 'resize': 'fit'}, 'medium': {'w': 1200, 'h': 675, 'resize': 'fit'}, 'thumb': {'w': 150, 'h': 150, 'resize': 'crop'}, 'small': {'w': 680, 'h': 383, 'resize': 'fit'}}, 'source_status_id': 1520564817349677000, 'source_status_id_str': '1520564817349677057', 'source_user_id': 793854134, 'source_user_id_str': '793854134'}, {'id': 1520564805534290000, 'id_str': '1520564805534289920', 'indices': [23, 46], 'media_url': 'http://pbs.twimg.com/media/FRohomPVEAASBwR.jpg', 'media_url_https': 'https://pbs.twimg.com/media/FRohomPVEAASBwR.jpg', 'url': 'https://t.co/zSDV208rAG', 'display_url': 'pic.twitter.com/zSDV208rAG', 'expanded_url': 'https://twitter.com/Takano_Shiho/status/1520564817349677057/photo/1', 'type': 'photo', 'sizes': {'thumb': {'w': 150, 'h': 150, 'resize': 'crop'}, 'small': {'w': 680, 'h': 383, 'resize': 'fit'}, 'medium': {'w': 1200, 'h': 675, 'resize': 'fit'}, 'large': {'w': 2048, 'h': 1152, 'resize': 'fit'}}, 'source_status_id': 1520564817349677000, 'source_status_id_str': '1520564817349677057', 'source_user_id': 793854134, 'source_user_id_str': '793854134'}, {'id': 1520564805609791500, 'id_str': '1520564805609791488', 'indices': [23, 46], 'media_url': 'http://pbs.twimg.com/media/FRohomhVIAAQSrm.jpg', 'media_url_https': 'https://pbs.twimg.com/media/FRohomhVIAAQSrm.jpg', 'url': 'https://t.co/zSDV208rAG', 'display_url': 'pic.twitter.com/zSDV208rAG', 'expanded_url': 'https://twitter.com/Takano_Shiho/status/1520564817349677057/photo/1', 'type': 'photo', 'sizes': {'medium': {'w': 1200, 'h': 675, 'resize': 'fit'}, 'large': {'w': 2048, 'h': 1152, 'resize': 'fit'}, 'thumb': {'w': 150, 'h': 150, 'resize': 'crop'}, 'small': {'w': 680, 'h': 383, 'resize': 'fit'}}, 'source_status_id': 1520564817349677000, 'source_status_id_str': '1520564817349677057', 'source_user_id': 793854134, 'source_user_id_str': '793854134'}]}, 'source': '<a href="http://twitter.com/download/iphone" rel="nofollow">Twitter for iPhone</a>', 'in_reply_to_status_id': None, 'in_reply_to_status_id_str': None, 'in_reply_to_user_id': None, 'in_reply_to_user_id_str': None, 'in_reply_to_screen_name': None, 'geo': None, 'coordinates': None, 'place': None, 'contributors': None, 'retweeted_status': {'created_at': 'Sun May 01 00:44:37 +0000 2022', 'id': 1520564817349677000, 'id_str': '1520564817349677057', 'text': '夏の宝石 https://t.co/zSDV208rAG', 'truncated': False, 'extended_entities': {'media': [{'id': 1520564805353885700, 'id_str': '1520564805353885696', 'indices': [5, 28], 'media_url': 'http://pbs.twimg.com/media/FRoholkUUAA3rbM.jpg', 'media_url_https': 'https://pbs.twimg.com/media/FRoholkUUAA3rbM.jpg', 'url': 'https://t.co/zSDV208rAG', 'display_url': 'pic.twitter.com/zSDV208rAG', 'expanded_url': 'https://twitter.com/Takano_Shiho/status/1520564817349677057/photo/1', 'type': 'photo', 'sizes': {'thumb': {'w': 150, 'h': 150, 'resize': 'crop'}, 'small': {'w': 680, 'h': 383, 'resize': 'fit'}, 'medium': {'w': 1200, 'h': 675, 'resize': 'fit'}, 'large': {'w': 2048, 'h': 1152, 'resize': 'fit'}}}, {'id': 1520564805475602400, 'id_str': '1520564805475602434', 'indices': [5, 28], 'media_url': 'http://pbs.twimg.com/media/FRohomBVkAIzaq-.jpg', 'media_url_https': 'https://pbs.twimg.com/media/FRohomBVkAIzaq-.jpg', 'url': 'https://t.co/zSDV208rAG', 'display_url': 'pic.twitter.com/zSDV208rAG', 'expanded_url': 'https://twitter.com/Takano_Shiho/status/1520564817349677057/photo/1', 'type': 'photo', 'sizes': {'large': {'w': 2048, 'h': 1152, 'resize': 'fit'}, 'medium': {'w': 1200, 'h': 675, 'resize': 'fit'}, 'thumb': {'w': 150, 'h': 150, 'resize': 'crop'}, 'small': {'w': 680, 'h': 383, 'resize': 'fit'}}}, {'id': 1520564805534290000, 'id_str': '1520564805534289920', 'indices': [5, 28], 'media_url': 'http://pbs.twimg.com/media/FRohomPVEAASBwR.jpg', 'media_url_https': 'https://pbs.twimg.com/media/FRohomPVEAASBwR.jpg', 'url': 'https://t.co/zSDV208rAG', 'display_url': 'pic.twitter.com/zSDV208rAG', 'expanded_url': 'https://twitter.com/Takano_Shiho/status/1520564817349677057/photo/1', 'type': 'photo', 'sizes': {'thumb': {'w': 150, 'h': 150, 'resize': 'crop'}, 'small': {'w': 680, 'h': 383, 'resize': 'fit'}, 'medium': {'w': 1200, 'h': 675, 'resize': 'fit'}, 'large': {'w': 2048, 'h': 1152, 'resize': 'fit'}}}, {'id': 1520564805609791500, 'id_str': '1520564805609791488', 'indices': [5, 28], 'media_url': 'http://pbs.twimg.com/media/FRohomhVIAAQSrm.jpg', 'media_url_https': 'https://pbs.twimg.com/media/FRohomhVIAAQSrm.jpg', 'url': 'https://t.co/zSDV208rAG', 'display_url': 'pic.twitter.com/zSDV208rAG', 'expanded_url': 'https://twitter.com/Takano_Shiho/status/1520564817349677057/photo/1', 'type': 'photo', 'sizes': {'medium': {'w': 1200, 'h': 675, 'resize': 'fit'}, 'large': {'w': 2048, 'h': 1152, 'resize': 'fit'}, 'thumb': {'w': 150, 'h': 150, 'resize': 'crop'}, 'small': {'w': 680, 'h': 383, 'resize': 'fit'}}}]}, 'source': '<a href="http://twitter.com/download/iphone" rel="nofollow">Twitter for iPhone</a>', 'in_reply_to_status_id': None, 'in_reply_to_status_id_str': None, 'in_reply_to_user_id': None, 'in_reply_to_user_id_str': None, 'in_reply_to_screen_name': None, 'geo': None, 'coordinates': None, 'place': None, 'contributors': None, 'is_quote_status': False, 'retweet_count': 76, 'favorite_count': 589, 'favorited': False, 'retweeted': False, 'possibly_sensitive': False, 'lang': 'ja'}, 'is_quote_status': False, 'retweet_count': 76, 'favorite_count': 0, 'favorited': False, 'retweeted': False, 'possibly_sensitive': False, 'lang': 'ja'}, 'contributors_enabled': False, 'is_translator': False, 'is_translation_enabled': False, 'profile_background_color': 'F5F8FA', 'profile_background_image_url': None, 'profile_background_image_url_https': None, 'profile_background_tile': False, 'profile_image_url': 'http://pbs.twimg.com/profile_images/1511726514634588166/0L3xPaQ4_normal.jpg', 'profile_image_url_https': 'https://pbs.twimg.com/profile_images/1511726514634588166/0L3xPaQ4_normal.jpg', 'profile_banner_url': 'https://pbs.twimg.com/profile_banners/1346478670240616449/1650041603', 'profile_link_color': '1DA1F2', 'profile_sidebar_border_color': 'C0DEED', 'profile_sidebar_fill_color': 'DDEEF6', 'profile_text_color': '333333', 'profile_use_background_image': True, 'has_extended_profile': True, 'default_profile': True, 'default_profile_image': False, 'following': None, 'follow_request_sent': None, 'notifications': None, 'translator_type': 'none', 'withheld_in_countries': []}
正しくパースされました。
トップページに戻る(=^・ω・^=)