最近,CSV ファイルから YAML 形式の設定ファイルを自動生成するスクリプトを書いていた.多重ネストされた YAML ファイルを Python オブジェクトとして読み込んだり,逆に Python オブジェクトを YAML ファイルに dump したりしようとする処理を書いていたところ,自分が実現したいファイルの構造を思うように生成できずにハマった.
yaml の 5 重ネストで訳わかんなくなってる
— mathnuko (@mathnuko) May 13, 2020
忘れた頃に同じハマり方をしそうなので,いくつかの YAML ファイルの例とともに,YAML ファイル <-> Python の辞書型・リスト型の相互変換の方法をチートシートっぽくまとめた.加えて,「YAML ファイル <-> Python の辞書型・リスト型の相互変換」だけでなく,「YAML ファイル <-> JSON ファイルのオブジェクト・配列の相互変換」も扱った.YAML,JSON に共通するのは「階層的な構造」であり,両者は相互変換可能であるため,ここで扱って汎用的な知識にしておくのが良いと判断したからである(相互変換する状況があるかは別にして).
要は本記事で 「YAML ファイル <-> key-value 型,配列型の相互変換」がどのようになっているかが理解できれば良い.
※ かなり長いため,「なんもわからん」人(本当に YAML について何も分からない人)以外は目次から必要な内容を探して読むのが良い.
本記事の目標
本記事の目標は,以下 2 つの項目を理解できるようになることである.
つまり,目標を 1 つにまとめると,以下になる.
- YAML ファイルを見たときにその構造を適切に把握できるようになる
開発環境
実行環境
- Python 3.8.0
- PyYAML 5.3.1
PyYAML は,Python で YAML ファイルを扱うためのパッケージである(デファクト?3rd パーティ製のパッケージをインストールするときに依存関係で見ることが多い気がする).標準モジュールでは無いためインストールする必要がある.
$ pip install PyYAML
使用ツール
YAML-online-parser.appspot.com
YAML の基本
まずは YAML の基本.
key: value
<-> 辞書型
辞書型に変換される.ここでは対応する JSON として示すが,「オブジェクト = Python の辞書型」,「配列 = Python のリスト型」にそれぞれ置き換えれば OK.
key: value
- 対応する JSON
{ "key": "value" }
- key: value
<-> リスト型
-
を key: value
の前に置くと,リスト型に変換される.つまり,-
は配列(リスト)の 1 つの要素に対応する.
- key: value
- 対応する JSON
key: value
が配列(リスト)の一つの要素になっていることが分かる.
[ { "key": "value" } ]
YAML ファイル内でのコメント
行の先頭に #
を付ければコメントになる.
# コメント key: value
複数行のコメントは無いため,複数行のコメントを行いたい場合は 1 行のコメントを連ねる必要がある.
# 複数行の # コメントは # 無い key: value
改行
以下を参照.
インデント
YAML ファイルはインデントが揃っていないと読み込みエラーになる.読み込み時のエラーは大体インデントが原因.
インデントにタブ文字は使えないため注意.
サンプルを見てみる:docker-compose.yml
上述した辞書型とリスト型を基本とし,YAML ファイルは設定ファイルなどで利用される.
例として,筆者が手元で利用している docker-compose.yml
を見てみる.ここでは,中身を理解する必要は全くなく,ただ YAML ファイルという「一定のルールで構造化されたテキストファイル」という観点で見れば良い.
この例で示すような「複雑にネストされた YAML <-> Python 辞書型・リスト型の変換」を自由自在に操れるようになるのが本記事の目標である.
version: "3" services: db: container_name: atcoder-stream-db image: postgres:12.1 expose: - "5432" web-backend: container_name: atcoder-stream-backend build: context: ./atcoder-stream-backend dockerfile: Dockerfile volumes: - ./atcoder-stream-backend/atcoder-stream-api:/app/src/atcoder-stream-api - ./atcoder-stream-backend/libraries/lib/lib:/app/src/libraries/lib - ./atcoder-stream-backend/libraries/twitterapi/twitterapi:/app/src/libraries/twitterapi ports: # host:container - "8000:8000" depends_on: - db command: sh -c "python /app/src/atcoder-stream-api/manage.py migrate && python /app/src/atcoder-stream-api/manage.py runserver 0.0.0.0:8000" web-frontend: container_name: atcoder-stream-frontend build: context: ./atcoder-stream-frontend dockerfile: Dockerfile volumes: - ./atcoder-stream-frontend:/app ports: # host:container - "3000:3000" command: sh -c "cd /app && yarn start"
- 対応する JSON
{ "version": "3", "services": { "db": { "container_name": "atcoder-stream-db", "image": "postgres:12.1", "expose": ["5432"] }, "web-backend": { "container_name": "atcoder-stream-backend", "build": { "context": "./atcoder-stream-backend", "dockerfile": "Dockerfile" }, "volumes": [ "./atcoder-stream-backend/atcoder-stream-api:/app/src/atcoder-stream-api", "./atcoder-stream-backend/libraries/lib/lib:/app/src/libraries/lib", "./atcoder-stream-backend/libraries/twitterapi/twitterapi:/app/src/libraries/twitterapi" ], "ports": ["8000:8000"], "depends_on": ["db"], "command": "sh -c \"python /app/src/atcoder-stream-api/manage.py migrate && python /app/src/atcoder-stream-api/manage.py runserver 0.0.0.0:8000\"" }, "web-frontend": { "container_name": "atcoder-stream-frontend", "build": { "context": "./atcoder-stream-frontend", "dockerfile": "Dockerfile" }, "volumes": ["./atcoder-stream-frontend:/app"], "ports": ["3000:3000"], "command": "sh -c \"cd /app && yarn start\"" } } }
ここで,YAML,JSON 両者を並べた画像を載せておく.各ファイルの構造がどのように対応しているかを確認しておくと良い.
注意:本記事での用語の扱い方
本記事では,Python を中心に据えるため,YAML における key-value 型の表現を「辞書型」,配列型の表現を「リスト型」と表現する(単に言い換えが面倒なので).実際には,それぞれの言語,形式で呼び方が異なるが,本記事では全て「辞書型」,「リスト型」で統一する.
- 実際に使われる用語と対応
正確には,「『YAML ファイルに定義された連想配列』を Python の辞書型に変換する」と言わなければいけないが,冗長になってしまうため,本記事では「『YAML ファイルの辞書型』を Python で読み込む」など簡易的な表現を使う.
実践例
ここからは,YAML ファイル <-> Python の変数の相互変換のさまざまな例を見ていく.各例について以下の 4 つを示し,相互変換の理解を深める.
ちなみに,本記事では YAML <-> Python が主であり,Python で JSON ファイルを扱う方法については触れていないため,それが知りたい方は手前味噌だが以下を参照すると良い.
ネストなし
YAML <-> 辞書型
- YAML
no-nest-dict.yml
id: 3141592 name: pyteyon main-language: Python blog: プログラミング日記
上記 YAML ファイルのインライン表記
{ id: 3141592, name: pyteyon, main-language: Python, blog: プログラミング日記 }
{ "id": 3141592, "name": "pyteyon", "main-language": "Python", "blog": "プログラミング日記" }
参考
LIFE WITH PYTHON | Python Tips: JSON を整形して表示したい python で JSON を扱うとき,日本語をエスケープさせない方法(2014/03, Qiita) Python においての JSON ファイルの取扱いあれこれ(2018/07, Qiita)
- Python:load
(pprint
は辞書型,リスト型を整形して出力するためだけのもの)
import yaml from pprint import pprint with open(file='./no-nest-dict.yml', mode='r', encoding='utf-8') as f: dic = yaml.load(f) pprint(dic, width=40) """ 出力 {'blog': 'プログラミング日記', 'id': 3141592, 'main-language': 'Python', 'name': 'pyteyon'} """
- Python:dump
import yaml data = { 'id': 3141592, 'main-language': 'Python', 'name': 'pyteyon', 'blog': 'プログラミング日記' } with open(file='./no-nest-dict.yml', mode='w', encoding='utf-8') as f: yaml.dump( data=data, stream=f, allow_unicode=True, # Unicode 文字をデコードされた文字列で表現 sort_keys=False )
補足:yaml.dump の キーワード引数
念のため,ここで利用する YAML.dump
関数の 2 つのキーワード引数を説明しておく.詳しくはドキュメントを読むと良い.
allow_unicode
sort_keys
- key でソートさせないようにする.
YAML.dump
では dump する際に,辞書型の key をアルファベット順にソートしてしまう.自分が辞書型に登録した key の順序で YAML に出力させたい場合,このオプションをFalse
にする.
allow_unicode
allow_unicode=False
id: 3141592 main-language: Python name: pyteyon blog: "\u30D7\u30ED\u30B0\u30E9\u30DF\u30F3\u30B0\u65E5\u8A18"
allow_unicode=True
デコードされた Unicode 文字が出力される
id: 3141592 main-language: Python name: pyteyon blog: プログラミング日記
sort_keys
sort_keys=False
id: 3141592 main-language: Python name: pyteyon blog: プログラミング日記
sort_keys=True
key でソートされた YAML ファイルが dump される.
blog: プログラミング日記 id: 3141592 main-language: Python name: pyteyon
これ以降は,以下の設定を前提にする.
YAML <-> リスト型
- YAML
no-nest-list.yml
- id - main-language - name - blog
インライン表記
["id", "main-language", "name", "blog"]
["id", "main-language", "name", "blog"]
- Python: load
import yaml from pprint import pprint with open(file='./no-nest-list.yml', mode='r', encoding='utf-8') as f: list_yaml = yaml.load(f) pprint(list_yaml, width=40) """ 出力 ['id', 'main-language', 'name', 'blog'] """
- Python: dump
import yaml data = { 'id': 3141592, 'main-language': 'Python', 'name': 'pyteyon', 'blog': 'プログラミング日記' } with open(file='./no-nest-dict.yml', mode='w', encoding='utf-8') as f: yaml.dump( data=data, stream=f, allow_unicode=True, sort_keys=False )
読み込みエラー:同じ階層に辞書型・リスト型を置く
YAML ファイル内で辞書型・リスト型に変換される表現を同じ階層に置くと,読み込み時にエラーが起こる.
- YAML(読み込みエラーになる)
no-nest-dict-and-list.yml
# 辞書型に変換される表現 id: 3141592 main-language: Python # リスト型に変換される表現 - name: pyteyon - blog: プログラミング日記
- JSON(読み込みエラーになる)
2 つの要素になってしまっている.
{ "id": 3141592, "main-language": "Python", } [ {"name": "pyteyon"}, {"blog": "プログラミング日記"}, ]
- Python: load
ParseError
になる.
import yaml with open(file='./no-nest-dict-and-list.yml', mode='r', encoding='utf-8') as f: obj = yaml.load(f) """ 出力 ParserError: while parsing a block mapping in "./no-nest/no-nest-dict-and-list.yml", line 2, column 1 expected <block end>, but found '-' in "./no-nest/no-nest-dict-and-list.yml", line 6, column 1 """
上記のエラーから分かるように,YAML ファイルは必ず「辞書型,またはリスト型のどちらかの型に変換可能な表現」でなければならない.つまり,変換の際,ネストの最上位は必ず {}
または []
で囲われた形になる(JSON もこれ).
尚,以下の形は可能である.
- 辞書型の key,value にリスト型が使われる
- リスト型の中身に辞書型が使われる
ネストの最上位に二つの要素が存在することがなければ問題なく読み込むことができる.
ネストあり:2 重ネスト
辞書型
dict-in-dict:辞書型の要素に辞書型
辞書型の要素に辞書型を使う.インデントのみで辞書型の要素を辞書型にできる.
- YAML
nest-2-dict-in-dict.yml
id: 3141592 name: pyteyon twitter-profile: screen-name: mathnuko username: mathnuko start: March 2018 tweets: 5596
インライン表記
id: 3141592 name: pyteyon twitter-profile: # 改行できる { screen-name: mathnuko, username: mathnuko, start: March 2018, tweets: 5596 }
{ "id": 3141592, "name": "pyteyon", "twitter-profile": { "screen-name": "mathnuko", "username": "mathnuko", "start": "March 2018", "tweets": 5596 } }
- Python: load
import yaml from pprint import pprint with open(file='./nest-2-dict-in-dict.yml', mode='r', encoding='utf-8') as f: obj = yaml.load(f) pprint(obj, width=40) """ 出力 {'id': 3141592, 'name': 'pyteyon', 'twitter-profile': {'screen-name': 'mathnuko', 'start': 'March ' '2018', 'tweets': 5596, 'username': 'mathnuko'}} """
- Python: dump
import yaml data = { 'experienced-languages': [ 'Python', 'JavaScript', 'C++' ], 'id': 3141592, 'name': 'pyteyon' } with open(file='./nest-2-dict-in-dict.yml`', mode='w', encoding='utf-8') as f: yaml.dump( data=data, stream=f, allow_unicode=True, sort_keys=False )
list-in-dict:辞書型の要素にリスト型
辞書型の要素にリスト型を使う.
- YAML
nest-2-list-in-dict.yml
id: 3141592 name: mathnuko experienced-languages: - Python - JavaScript - C++
インライン表記
id: 3141592 name: mathnuko experienced-languages: [Python, JavaScript, C++]
{ "id": 3141592, "name": "mathnuko", "experienced-languages": [ "Python", "JavaScript", "C++" ] }
- Python: load
import yaml from pprint import pprint with open(file='./nest-2-list-in-dict.yml', mode='r', encoding='utf-8') as f: obj = yaml.load(f) pprint(obj, width=40) """ 出力 {'experienced-languages': ['Python', 'JavaScript', 'C++'], 'id': 3141592, 'name': 'pyteyon'} """
- Python: dump
import yaml data = { 'experienced-languages': [ 'Python', 'JavaScript', 'C++' ], 'id': 3141592, 'name': 'pyteyon' } with open(file='./nest-2-list-in-dict.yml', mode='w', encoding='utf-8') as f: yaml.dump( data=data, stream=f, allow_unicode=True, sort_keys=False )
リスト型
dict-in-list:リスト型の要素に辞書型
- YAML
nest-2-dict-in-list.yml
- id: 3141592 - main-language: Python - name: pyteyon - blog: プログラミング日記
インライン表記
[id: 3141592, main-language: Python, name: pyteyon, blog: プログラミング日記]
[ { "id": 3141592 }, { "main-language": "Python" }, { "name": "pyteyon" }, { "blog": "プログラミング日記" } ]
- Python: load
import yaml from pprint import pprint with open(file='./nest-2-dict-in-list.yml', mode='r', encoding='utf-8') as f: obj = yaml.load(f) pprint(obj, width=40) """ 出力 [{'id': 3141592}, {'main-language': 'Python'}, {'name': 'pyteyon'}, {'blog': 'プログラミング日記'}] """
- Python: dump
import yaml data = [ {'id': 3141592}, {'main-language': 'Python'}, {'name': 'pyteyon'}, {'blog': 'プログラミング日記'} ] with open(file='./nest-2-dict-in-list.yml', mode='w', encoding='utf-8') as f: yaml.dump( data=data, stream=f, allow_unicode=True, sort_keys=False )
list-in-list:リスト型の要素にリスト型
- YAML
nest-2-list-in-list.yml
- - Python - JavaScript - Ruby - - Django - Flask - Express - Ruby on Rails - - unittest - pytest - Jest - RSpec
インライン表記
- [Python, JavaScript, Ruby] - [Django, Flask, Express, Ruby on Rails] - [unittest, pytest, Jest, Rspec]
[ [ "Python", "JavaScript", "Ruby" ], [ "Django", "Flask", "Express", "Ruby on Rails" ], [ "unittest", "pytest", "Jest", "Rspec" ] ]
- Python: load
import yaml from pprint import pprint with open(file='./nest-2-list-in-list.yml', mode='r', encoding='utf-8') as f: obj = yaml.load(f) pprint(obj, width=40) """ 出力 [['Python', 'JavaScript', 'Ruby'], ['Django', 'Flask', 'Express', 'Ruby on Rails'], ['unittest', 'pytest', 'Jest', 'Rspec']] """
- Python: dump
import yaml data = [ ['Python', 'JavaScript', 'Ruby'], ['Django', 'Flask', 'Express', 'Ruby on Rails'], ['unittest', 'pytest', 'Jest', 'Rspec'] ] with open(file='./nest-2-list-in-list.yml', mode='w', encoding='utf-8') as f: yaml.dump( data=data, stream=f, allow_unicode=True, sort_keys=False )
ネストあり:3 重以上のネスト
最後に,3 重以上のネストがある YAML ファイルを扱う.
3 重以上のネストがある YAML ファイルでは,非常に多くの組み合わせがあり複雑さも増すため,体系的にまとめずにいくつかの例を示すことにする.
ただし,ネストの複雑さが増すと言っても,結局はここまでで扱った単純な構造の組み合わせであるため,構造を丁寧に分解していけば尻込みすることはない.
例 1:docker-compose.yml(再掲)
- YAML
docker-compose.yml
version: "3" services: db: container_name: atcoder-stream-db image: postgres:12.1 expose: - "5432" web-backend: container_name: atcoder-stream-backend build: context: ./atcoder-stream-backend dockerfile: Dockerfile volumes: - ./atcoder-stream-backend/atcoder-stream-api:/app/src/atcoder-stream-api - ./atcoder-stream-backend/libraries/lib/lib:/app/src/libraries/lib - ./atcoder-stream-backend/libraries/twitterapi/twitterapi:/app/src/libraries/twitterapi ports: - "8000:8000" depends_on: - db command: sh -c "python /app/src/atcoder-stream-api/manage.py migrate && python /app/src/atcoder-stream-api/manage.py runserver 0.0.0.0:8000" web-frontend: container_name: atcoder-stream-frontend build: context: ./atcoder-stream-frontend dockerfile: Dockerfile volumes: - ./atcoder-stream-frontend:/app ports: - "3000:3000" command: sh -c "cd /app && yarn start"
{ "version": "3", "services": { "db": { "container_name": "atcoder-stream-db", "image": "postgres:12.1", "expose": ["5432"] }, "web-backend": { "container_name": "atcoder-stream-backend", "build": { "context": "./atcoder-stream-backend", "dockerfile": "Dockerfile" }, "volumes": [ "./atcoder-stream-backend/atcoder-stream-api:/app/src/atcoder-stream-api", "./atcoder-stream-backend/libraries/lib/lib:/app/src/libraries/lib", "./atcoder-stream-backend/libraries/twitterapi/twitterapi:/app/src/libraries/twitterapi" ], "ports": ["8000:8000"], "depends_on": ["db"], "command": "sh -c \"python /app/src/atcoder-stream-api/manage.py migrate && python /app/src/atcoder-stream-api/manage.py runserver 0.0.0.0:8000\"" }, "web-frontend": { "container_name": "atcoder-stream-frontend", "build": { "context": "./atcoder-stream-frontend", "dockerfile": "Dockerfile" }, "volumes": ["./atcoder-stream-frontend:/app"], "ports": ["3000:3000"], "command": "sh -c \"cd /app && yarn start\"" } } }
- Python オブジェクト
data = { 'version': '3', 'services': { 'db': { 'container_name': 'atcoder-stream-db', 'expose': ['5432'], 'image': 'postgres:12.1' }, 'web-backend': { 'build': { 'context': './atcoder-stream-backend', 'dockerfile': 'Dockerfile' }, 'command': 'sh ' '-c ' '"python ' '/app/src/atcoder-stream-api/manage.py ' 'migrate ' '&& ' 'python ' '/app/src/atcoder-stream-api/manage.py ' 'runserver ' '0.0.0.0:8000"', 'container_name': 'atcoder-stream-backend', 'depends_on': ['db'], 'ports': ['8000:8000'], 'volumes': [ './atcoder-stream-backend/atcoder-stream-api:/app/src/atcoder-stream-api', './atcoder-stream-backend/libraries/lib/lib:/app/src/libraries/lib', './atcoder-stream-backend/libraries/twitterapi/twitterapi:/app/src/libraries/twitterapi' ] }, 'web-frontend': { 'build': { 'context': './atcoder-stream-frontend', 'dockerfile': 'Dockerfile' }, 'command': 'sh ' '-c ' '"cd ' '/app ' '&& ' 'yarn ' 'start"', 'container_name': 'atcoder-stream-frontend', 'ports': ['3000:3000'], 'volumes': ['./atcoder-stream-frontend:/app'] } } }
例 2:language-list.yml
プログラミング言語のリストを表す YAML ファイルを作ってみた.遊びで書いたので内容は間違いがあるかもしれない.
- YAML
language-list.yml
# programming language list - language: Python information: creator: Guido van Rossum first-release: 1991 official-page: https://www.python.org/ features: type-system: dynamic paradigm: - object-oriented # オブジェクト指向 - imperative # 命令型 - procedural # 手続き型(=命令型) memory: managed web-framework: - name: Django type: full-stack documentation: https://docs.djangoproject.com/en/3.0/ - name: Flask type: micro documentation: https://flask.palletsprojects.com/en/1.1.x/ - name: FastAPI type: micro documentation: https://fastapi.tiangolo.com/ test-framework: - unittest - pytest - nose - language: Ruby information: creator: Yukihiro "Matz" Matsumoto first-release: 1995 official-page: https://www.ruby-lang.org/en/ features: type-system: dynamic paradigm: - object-oriented - imperative - procedural memory: managed web-framework: - name: Ruby on Rails type: full-stack documentation: https://rubyonrails.org/ - name: Sinatra type: micro documentation: http://sinatrarb.com/ - name: Cuba type: micro documentation: https://cuba.is/ test-framework: - RSpec - "Test::Unit" - Cucumber - language: JavaScript information: creator: Brendan Eich first-release: 1997 official-page: https://www.ecma-international.org/ecma-262/ developer-page: https://developer.mozilla.org/en/JavaScript features: type-system: dynamic paradigm: - object-oriented - imperative - procedural memory: managed framework: backend: - name: Express.js type: micro documentation: http://expressjs.com/ - name: Meteor.js type: micro documentation: https://www.meteor.com/ frontend: - name: React.js documentation: https://ja.reactjs.org/ - name: Vue.js documentation: https://vuejs.org/index.html - name: Angular.js documentation: https://angularjs.org/ test-framework: backend: - Mocha - AVA frontend: - Jest - Jasmine - language: Java information: creator: James Arthur Gosling(Sun Microsystems, merged to Oracle in 2010) first-release: 1995 official-page: https://www.oracle.com/technetwork/java/javase/documentation/index.html features: type-system: static paradigm: - object-oriented - imperative - procedural memory: managed web-framework: - name: Spring Framework type: full-stack documentation: https://projects.spring.io/spring-framework/ - name: Play Framework type: full-stack documentation: https://www.playframework.com/ inspired: - language: Ruby framework: Ruby on Rails - language: Python framework: Django - name: Struts type: full-stack documentation: https://struts.apache.org/ test-framework: - JUnit - TestNG
[ { "language": "Python", "information": { "creator": "Guido van Rossum", "first-release": 1991, "official-page": "https://www.python.org/" }, "features": { "type-system": "dynamic", "paradigm": ["object-oriented", "imperative", "procedural"], "memory": "managed", "web-framework": [ { "name": "Django", "type": "full-stack", "documentation": "https://docs.djangoproject.com/en/3.0/" }, { "name": "Flask", "type": "micro", "documentation": "https://flask.palletsprojects.com/en/1.1.x/" }, { "name": "FastAPI", "type": "micro", "documentation": "https://fastapi.tiangolo.com/" } ], "test-framework": ["unittest", "pytest", "nose"] } }, { "language": "Ruby", "information": { "creator": "Yukihiro \"Matz\" Matsumoto", "first-release": 1995, "official-page": "https://www.ruby-lang.org/en/" }, "features": { "type-system": "dynamic", "paradigm": ["object-oriented", "imperative", "procedural"], "memory": "managed", "web-framework": [ { "name": "Ruby on Rails", "type": "full-stack", "documentation": "https://rubyonrails.org/" }, { "name": "Sinatra", "type": "micro", "documentation": "http://sinatrarb.com/" }, { "name": "Cuba", "type": "micro", "documentation": "https://cuba.is/" } ], "test-framework": ["RSpec", "Test::Unit", "Cucumber"] } }, { "language": "JavaScript", "information": { "creator": "Brendan Eich", "first-release": 1997, "official-page": "https://www.ecma-international.org/ecma-262/", "developer-page": "https://developer.mozilla.org/en/JavaScript" }, "features": { "type-system": "dynamic", "paradigm": ["object-oriented", "imperative", "procedural"], "memory": "managed", "framework": { "backend": [ { "name": "Express.js", "type": "micro", "documentation": "http://expressjs.com/" }, { "name": "Meteor.js", "type": "micro", "documentation": "https://www.meteor.com/" } ], "frontend": [ { "name": "React.js", "documentation": "https://ja.reactjs.org/" }, { "name": "Vue.js", "documentation": "https://vuejs.org/index.html" }, { "name": "Angular.js", "documentation": "https://angularjs.org/" } ] }, "test-framework": { "backend": ["Mocha", "AVA"], "frontend": ["Jest", "Jasmine"] } } }, { "language": "Java", "information": { "creator": "James Arthur Gosling(Sun Microsystems, merged to Oracle in 2010)", "first-release": 1995, "official-page": "https://www.oracle.com/technetwork/java/javase/documentation/index.html" }, "features": { "type-system": "static", "paradigm": ["object-oriented", "imperative", "procedural"], "memory": "managed", "web-framework": [ { "name": "Spring Framework", "type": "full-stack", "documentation": "https://projects.spring.io/spring-framework/" }, { "name": "Play Framework", "type": "full-stack", "documentation": "https://www.playframework.com/", "inspired": [ { "language": "Ruby", "framework": "Ruby on Rails" }, { "language": "Python", "framework": "Django" } ] }, { "name": "Struts", "type": "full-stack", "documentation": "https://struts.apache.org/" } ], "test-framework": ["JUnit", "TestNG"] } } ]
- Python オブジェクト
language_list = [ # Python { 'language': 'Python', 'information': { 'creator': 'Guido van Rossum', 'first-release': 1991, 'official-page': 'https://www.python.org/' }, 'features': { 'memory': 'managed', 'paradigm': [ 'object-oriented', 'imperative', 'procedural' ], 'test-framework': [ 'unittest', 'pytest', 'nose' ], 'type-system': 'dynamic', 'web-framework': [ { 'documentation': 'https://docs.djangoproject.com/en/3.0/', 'name': 'Django', 'type': 'full-stack' }, { 'documentation': 'https://flask.palletsprojects.com/en/1.1.x/', 'name': 'Flask', 'type': 'micro' }, { 'documentation': 'https://fastapi.tiangolo.com/', 'name': 'FastAPI', 'type': 'micro' } ] }, }, # Ruby { 'language': 'Ruby', 'information': { 'creator': 'Yukihiro "Matz" Matsumoto', 'first-release': 1995, 'official-page': 'https://www.ruby-lang.org/en/' }, 'features': { 'memory': 'managed', 'paradigm': [ 'object-oriented', 'imperative', 'procedural' ], 'test-framework': [ 'RSpec', 'Test::Unit', 'Cucumber' ], 'type-system': 'dynamic', 'web-framework': [ { 'documentation': 'https://rubyonrails.org/', 'name': 'Ruby ' 'on ' 'Rails', 'type': 'full-stack' }, { 'documentation': 'http://sinatrarb.com/', 'name': 'Sinatra', 'type': 'micro' }, { 'documentation': 'https://cuba.is/', 'name': 'Cuba', 'type': 'micro' } ] }, }, # JavaScript { 'language': 'JavaScript', 'information': { 'creator': 'Brendan Eich', 'developer-page': 'https://developer.mozilla.org/en/JavaScript', 'first-release': 1997, 'official-page': 'https://www.ecma-international.org/ecma-262/' }, 'features': { 'framework': { 'backend': [ { 'documentation': 'http://expressjs.com/', 'name': 'Express.js', 'type': 'micro' }, { 'documentation': 'https://www.meteor.com/', 'name': 'Meteor.js', 'type': 'micro' } ], 'frontend': [ { 'documentation': 'https://ja.reactjs.org/', 'name': 'React.js' }, { 'documentation': 'https://vuejs.org/index.html', 'name': 'Vue.js' }, { 'documentation': 'https://angularjs.org/', 'name': 'Angular.js' } ] }, 'memory': 'managed', 'paradigm': [ 'object-oriented', 'imperative', 'procedural' ], 'test-framework': { 'backend': [ 'Mocha', 'AVA' ], 'frontend': [ 'Jest', 'Jasmine' ] }, 'type-system': 'dynamic' }, }, # Java { 'language': 'Java', 'information': { 'creator': 'James Arthur Gosling(Sun Microsystems, merged to Oracle in 2010)', 'first-release': 1995, 'official-page': 'https://www.oracle.com/technetwork/java/javase/documentation/index.html' }, 'features': { 'memory': 'managed', 'paradigm': [ 'object-oriented', 'imperative', 'procedural' ], 'test-framework': [ 'JUnit', 'TestNG' ], 'type-system': 'static', 'web-framework': [ { 'documentation': 'https://projects.spring.io/spring-framework/', 'name': 'Spring ' 'Framework', 'type': 'full-stack' }, { 'documentation': 'https://www.playframework.com/', 'inspired': [ { 'framework': 'Ruby ' 'on ' 'Rails', 'language': 'Ruby' }, { 'framework': 'Django', 'language': 'Python' } ], 'name': 'Play ' 'Framework', 'type': 'full-stack' }, { 'documentation': 'https://struts.apache.org/', 'name': 'Struts', 'type': 'full-stack' } ] }, } ]
終わりに
複雑な YAML ファイルも単純な構造の組み合わせであると分かれば難しくはない.
内容の割りにかなりのボリュームなってしまったが,自分がハマったときに戻ってこれるチートシート的な記事が書けたので満足.長い記事に付き合ってくださった方はありがとうございました.
参考
特に 1 番上の記事は非常に参考になった.