برنامه‌نویسی

۶ قابلیت YAML که اکثر برنامه‌نویسان از آن‌ها بی‌اطلاع هستند

۶ قابلیت yaml که اکثر برنامه‌نویسان از آن‌ها بی‌اطلاع هستند

YAML یک فرمت فایل است که معمولا برای data serialization استفاده می‌شود. همچنین پروژه‌های زیاد وجود دارند که از فایل‌های YAML به‌منظور پیکربندی استفاده می‌کنند برای مثال می‌توانیم به Docker-compose، pre-commit, TravisCI، AWS Cloudformation، ESLint، Kubernetes، Ansible اشاره داشته باشیم. بنابراین آشنایی با قابلیت‌های YAML می‌تواند بسیار مفید باشد.

در ابتدا باید بدانید که YAML با توجه به این مقاله بسیاری قابلیت‌های JSON را در خود دارد. هر فایل معتبر JSON نیز یک فایل معتبر YAML است. این یعنی شما تمام نوع‌های متغیر مورد نیاز خود مانند integerها، floatها، stringها، booleanها، null و همچنین sequence‌ها و mapها را در اختیار دارید. به نسبت زبان برنامه‌نویسی که از آن استفاده می‌کنید ممکن است sequence را به‌عنوان array یا list و از map به‌عنوان dictionary استفاده کنید.

یک فایل YAML معمولا به شکل زیر است:

mysql:
  host: localhost
  user: root
  password: something
preprocessing_queue: # Line comments are available!
  - name: preprocessing.scale_and_center
    width: 32
    height: 32
  - preprocessing.dot_reduction
use_anonymous: true

Equivalent Notation

برای پیاده‌سازی فایل‌های YAML روش‌های معادل زیادی وجود دارد:

list_by_dash:
  - foo
  - bar
list_by_square_bracets: [foo, bar]
map_by_indentation:
  foo: bar
  bar: baz
map_by_curly_braces: { foo: bar, bar: baz }
string_no_quotes: Monty Python
string_double_quotes: "Monty Python"
string_single_quotes: 'Monty Python'
bool_english: yes
bool_english_no: no
bool_python: True
bool_json: true

یکی از مواردی که باید به آن توجه داشته باشید:

language: no # ISO 639-1 code for the Norwegian language

این no به‌معنای false است و برای نوشتن no می‌توانید آن را در double quotes یا single quotes بگذارید. البته به‌طور کلی توصیه می‌شود که از true و false برای مقادیر boolean استفاده شود اما در YAML می‌توانید به ۱۱ روش booleanها را مقداردهی کنید.

اگر می‌خواهید از quotes برای stringها استفاده کنید بهتر است از double quotes استفاده شود زیرا برای افراد تازه‌کاری که می‌خواهند با فایل‌های YAML کار کنند قابل درک‌تر است.

البته معادل‌های بسیار زیاد دیگری وجود دارد و همان‌طور که Tom Ritchford اشاره کرده است شاید این معادل‌های بسیار زیاد باعث شود تا فایل‌های YAML انتخابی خطرناک و نامناسب باشند.

Long Strings

disclaimer: >
  Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  In nec urna pellentesque, imperdiet urna vitae, hendrerit
  odio. Donec porta aliquet laoreet. Sed viverra tempus fringilla.

YAML فوق معادل JSON زیر است:

{ "disclaimer": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. In nec urna pellentesque, imperdiet urna vitae, hendrerit odio. Donec porta aliquet laoreet. Sed viverra tempus fringilla." }

Multi-Line String

mail_signature: |
  Martin Thoma
  Tel. +49 123 4567

YAML فوق معادل JSON زیر است:

{ "mail_signature": "Martin Thoma\nTel. +49 123 4567" }

Anchor

email: &emailAddress "info@example.de"
id: *emailAddress

YAML فوق معادل JSON زیر است:

{
    "email": "info@example.de",
    "id": "info@example.de"
}

با استفاده از & یک متغیر با نام emailAddress و مقدار info@example.de تعریف می‌شود و *emailAddress به مقدار متغیر تعریف شده اشاره می‌کند.

شما می‌توانید همین کار را با mapping انجام دهید:

foo: &default_settings
  db:
    host: localhost
    name: main_db
    port: 1337
  email:
    admin: admin@example.com
prod: *default_settings
dev: *default_settings

معادل:

{
    "dev": {
        "db": {
            "host": "localhost",
            "name": "main_db",
            "port": 1337
        },
        "email": {
            "admin": "admin@example.com"
        }
    },
    "foo": {
        "db": {
            "host": "localhost",
            "name": "main_db",
            "port": 1337
        },
        "email": {
            "admin": "admin@example.com"
        }
    },
    "prod": {
        "db": {
            "host": "localhost",
            "name": "main_db",
            "port": 1337
        },
        "email": {
            "admin": "admin@example.com"
        }
    }
}

ممکن است بخواهید که رمزعبور دیتابیس یا هر داده‌ی دیگری را به تنظیمات dev و prod اضافه کنید که با استفاده از merge key << می‌توان این کار را انجام داد.

foo: &default_settings
  db:
    host: localhost
    name: main_db
    port: 1337
  email:
    admin: admin@example.com
prod:
  <<: *default_settings
  app:
    port: 80
dev: *default_settings

YAML فوق معادل JSON زیر است:

{
    "foo": {
        "db": {
            "host": "localhost",
            "name": "main_db",
            "port": 1337
        },
        "email": {
            "admin": "admin@example.com"
        }
    },
    "prod": {
        "app": {
            "port": 80
        },
        "db": {
            "host": "localhost",
            "name": "main_db",
            "port": 1337
        },
        "email": {
            "admin": "admin@example.com"
        }
    },
    "dev": {
        "db": {
            "host": "localhost",
            "name": "main_db",
            "port": 1337
        },
        "email": {
            "admin": "admin@example.com"
        }
    }
}

Type Casting

!! در YAML معنای خاصی دارد و با توجه به این مستندات کوتاه‌ شده‌ی !tag:yaml.org,2002: است.

برای تبدیل نوع متغیر می‌توانید به‌صورت زیر عمل کنید:

price: !!float 42
id: !!str 42

یا در موارد پیچیده‌تر می‌توانیم داده‌ها را به انواع داده‌هایی که در زبان Python تعریف شده‌اند اما در YAML به‌صورت مستقیم وجود ندارند، تبدیل کنیم:

tuple_example: !!python/tuple
  - 1337
  - 42
set_example: !!set { 1337, 42 }
date_example: !!timestamp 2020-12-31

YAML فوق را می‌توان معادلی برای کدهای زیر دانست:

import yaml
import pprint

with open("example.yaml") as fp:
    data = fp.read()

pp = pprint.PrettyPrinter(indent=4)

pased = yaml.unsafe_load(data)
pp.pprint(pased)

که خروجی آن به‌صورت زیر است:

{   'date_example': datetime.date(2020, 12, 31),
    'set_example': set([42, 1337]),
    'tuple_example': (1337, 42)}

در این مثال از تگ مخصوص برای زبان Python یعنی !!python/tuple و برخی تگ‌های استاندارد YAML استفاده شده است. PyYaml اطلاعات بیشتری در این باره ارائه می‌دهد:

## Standard YAML tags
YAML               Python 3
!!null             None
!!bool             bool
!!int              int
!!float            float
!!binary           bytes
!!timestamp        datetime.datetime
!!omap, !!pairs    list of pairs
!!set              set
!!str              str
!!seq              list
!!map              dict## Python-specific tags
YAML               Python 3
!!python/none      None
!!python/bool      bool
!!python/bytes     bytes
!!python/str       str
!!python/unicode   str
!!python/int       int
!!python/long      int
!!python/float     float
!!python/complex   complex
!!python/list      list
!!python/tuple     tuple
!!python/dict      dict## Complex Python tags
!!python/name:module.name         module.name
!!python/module:package.module    package.module
!!python/object:module.cls        module.cls instance
!!python/object/new:module.cls    module.cls instance
!!python/object/apply:module.f    value of f(...)

توجه داشته باشید که load کردن تگ‌های غیر استاندارد، ایمن نیست! اما به‌هرصورت اجرای کدهای دلخواه با !!python/object/apply:module.f امکان‌پذیر است. در PyYaml به yaml.unsafe_load نیاز است تا بتوانید از آن استفاده کنید.

Multiple Documents در یک فایل YAML

شما می‌توانید با استفاده از سه - داکیومنت‌های جداگانه‌ای داشته باشید:

foo: bar
---
fizz: buzz

در Python می‌توانید به‌صورت زیر عمل کنید:

import yaml

with open("example.yaml") as fp:
    data = fp.read()

parsed = yaml.safe_load_all(data) # parsed is a generator

اگر متغیر parsed را به list تبدیل کرده و آن را print کنید، خروجی به شکل زیر خواهد بود:

[{'foo': 'bar'}, {'fizz': 'buzz'}]

توجه داشته باشید که این یک معادل جایگزین برای ایجاد list نیست.

منبع: https://levelup.gitconnected.com/6-yaml-features-most-programmers-dont-know-164762343af3