DevToys Pro

бесплатные веб-инструменты для разработчиков

Блог
Оцените нас:
Попробуйте расширение для браузера:
← Назад к блогу

Подводные камни неявной типизации YAML: Булевы значения, Null и ведущие нули

14 мин чтения

Вы конвертируете YAML в JSON и внезапно "yes" становится true, "07" становится 7, а "null" становится null. Неявное преобразование типов в YAML мощное, но может вызывать неожиданные баги, когда парсеры интерпретируют строки как булевы значения, числа или null. Это руководство объясняет правила вывода типов в YAML и как предотвратить нежелательные преобразования.

Проблема: YAML угадывает типы

В отличие от JSON, который требует явных типов (строки должны быть в кавычках, булевы значения это true/false), YAML выводит типы из контекста. Это удобно, но подвержено ошибкам.

Пример: Неожиданное преобразование типов

# Конфигурационный YAML файл
settings:
  enabled: yes           # Становится булевым true
  code: 007             # Становится числом 7
  value: null           # Становится null
  country: NO           # Становится булевым false!

При конвертации в JSON:

{
  "settings": {
    "enabled": true,      // Было: "yes"
    "code": 7,            // Было: "007" 
    "value": null,        // Было: "null"
    "country": false      // Было: "NO"
  }
}

Код страны NO (Норвегия) стал false, потому что YAML 1.1 трактует NO как булево значение. Это самый печально известный подводный камень YAML.

Булевы значения: Больше чем True/False

YAML 1.1 распознаёт 22 различных булевых значения. YAML 1.2 упростил это, но многие парсеры всё ещё используют правила YAML 1.1.

Булевы значения YAML 1.1

True: true, True, TRUE, yes, Yes, YES, on, On, ON, y, Y

False: false, False, FALSE, no, No, NO, off, Off, OFF, n, N

Булевы значения YAML 1.2 (Рекомендуется)

True: true, True, TRUE

False: false, False, FALSE

Реальный пример: Коды стран

# YAML профиля пользователя
users:
  - name: Alice
    country: US          # Строка (не булевое ключевое слово)
  
  - name: Bob
    country: NO          # YAML 1.1: false (булево!)
  
  - name: Carol
    country: yes         # YAML 1.1: true (булево!)

Конвертировано в JSON (YAML 1.1):

{
  "users": [
    {"name": "Alice", "country": "US"},
    {"name": "Bob", "country": false},     // Баг!
    {"name": "Carol", "country": true}     // Баг!
  ]
}

Решение: Заключите строковые значения в кавычки

users:
  - name: Bob
    country: "NO"        # Явная строка, не булево
  
  - name: Carol  
    country: "yes"       # Явная строка, не булево

Теперь конвертируется правильно:

{
  "users": [
    {"name": "Bob", "country": "NO"},
    {"name": "Carol", "country": "yes"}
  ]
}

Null значения: Много способов выразить ничто

YAML распознаёт множественные представления null:

  • null, Null, NULL
  • ~ (тильда)
  • Пустое значение (ключ без значения)

Пример: Представления Null

# Все эти становятся null в JSON
data:
  field1: null
  field2: NULL
  field3: ~
  field4:                # Пустое значение
  field5:                # Двоеточие в конце, нет значения

Конвертировано в JSON:

{
  "data": {
    "field1": null,
    "field2": null,
    "field3": null,
    "field4": null,
    "field5": null
  }
}

Подводный камень: Строка "null" без кавычек

# Конфигурация с nullable полем
database:
  password: null         # Null значение (нет пароля)
  
api_key: null            # Null значение (нет API ключа)

# Но что если вы буквально хотите строку "null"?
log_level: "null"        # Строка "null", не null значение

Конвертировано в JSON:

{
  "database": {
    "password": null      // Настоящий null
  },
  "api_key": null,        // Настоящий null
  "log_level": "null"     // Строка "null"
}

Ведущие нули: Ловушка восьмеричных чисел

Числа с ведущими нулями могут интерпретироваться как восьмеричные (основание-8) в YAML 1.1.

Пример: Восьмеричное преобразование

# Конфигурация с кодами
items:
  code1: 007             # Восьмеричное 007 = десятичное 7
  code2: 010             # Восьмеричное 010 = десятичное 8
  code3: 077             # Восьмеричное 077 = десятичное 63
  code4: 100             # Десятичное 100 (нет ведущего нуля)

Конвертировано в JSON (YAML 1.1):

{
  "items": {
    "code1": 7,           // Потеряны ведущие нули
    "code2": 8,           // Конвертировано из восьмеричного
    "code3": 63,          // Конвертировано из восьмеричного
    "code4": 100
  }
}

Реальная проблема: Телефонные номера и почтовые индексы

# Адресная книга
contacts:
  - name: Alice
    zip: 02134            # Почтовый индекс Бостона
  
  - name: Bob
    zip: 10001            # Почтовый индекс Нью-Йорка
  
  - name: Carol
    phone: 07700900123    # UK телефонный номер

Конвертировано в JSON (YAML 1.1):

{
  "contacts": [
    {"name": "Alice", "zip": 1116},        // Баг! Восьмеричное 02134 = десятичное 1116
    {"name": "Bob", "zip": 4097},          // Баг! Восьмеричное 10001 = десятичное 4097
    {"name": "Carol", "phone": 534643795}  // Баг! Потерян ведущий ноль
  ]
}

Решение: Заключите числовые строки в кавычки

contacts:
  - name: Alice
    zip: "02134"          # Явная строка
  
  - name: Bob
    zip: "10001"          # Явная строка
  
  - name: Carol
    phone: "07700900123"  # Явная строка

Теперь конвертируется правильно:

{
  "contacts": [
    {"name": "Alice", "zip": "02134"},
    {"name": "Bob", "zip": "10001"},
    {"name": "Carol", "phone": "07700900123"}
  ]
}

Шестнадцатеричные числа

YAML 1.1 распознаёт шестнадцатеричные числа с префиксом 0x:

colors:
  red: 0xFF0000          # Hex число = 16711680
  green: 0x00FF00        # Hex число = 65280
  blue: 0x0000FF         # Hex число = 255

Конвертировано в JSON:

{
  "colors": {
    "red": 16711680,
    "green": 65280,
    "blue": 255
  }
}

Если вы хотите сохранить hex строки (например, для CSS цветов), заключите их в кавычки:

colors:
  red: "#FF0000"         # Строка
  green: "#00FF00"       # Строка
  blue: "#0000FF"        # Строка

Научная нотация

YAML поддерживает научную нотацию для чисел с плавающей точкой:

measurements:
  distance: 1.23e5       # 123000
  tiny: 1.23e-5          # 0.0000123
  avogadro: 6.022e23     # 6.022 × 10^23

Конвертировано в JSON:

{
  "measurements": {
    "distance": 123000,
    "tiny": 0.0000123,
    "avogadro": 6.022e+23
  }
}

Бесконечность и NaN

YAML 1.1 поддерживает специальные значения с плавающей точкой:

special_numbers:
  positive_inf: .inf     # Положительная бесконечность
  negative_inf: -.inf    # Отрицательная бесконечность
  not_a_number: .nan     # NaN (Не число)

Эти конвертируются в специальные JSON значения (зависит от реализации). Большинство JSON библиотек не поддерживают их нативно и могут сериализовать их как строки или null.

Когда заключать значения в кавычки

Всегда заключайте в кавычки:

  • Коды стран (NO, YES, ON, OFF, N, Y)
  • Номера версий с ведущими нулями (01.02.03)
  • Телефонные номера с ведущими нулями
  • Почтовые индексы с ведущими нулями
  • Hex коды цветов (#FF0000)
  • Буквальные строки "true", "false", "null", "yes", "no", "on", "off"
  • Числа, которые должны оставаться строками

Не нужно заключать в кавычки:

  • Простые строки без специальных символов
  • Числа, которые вы хотите как числа
  • Настоящие булевы или null значения

Реальный пример: Docker Compose

Docker Compose файлы написаны на YAML и часто сталкиваются с этими подводными камнями:

# docker-compose.yml
version: "3.8"           # Кавычки! Иначе станет float 3.8

services:
  app:
    image: node:18
    environment:
      - NODE_ENV=production
      - PORT=3000
      - ENABLE_FEATURE=yes      # Становится булевым true
      - DEBUG=no                # Становится булевым false
    ports:
      - "3000:3000"      # Кавычки! Строка маппинга портов

Переменные окружения ENABLE_FEATURE и DEBUG становятся булевыми, что может не работать, если ваше приложение ожидает строковые значения.

Исправленная версия

version: "3.8"

services:
  app:
    image: node:18
    environment:
      - NODE_ENV=production
      - PORT=3000
      - ENABLE_FEATURE="yes"    # Явная строка
      - DEBUG="no"              # Явная строка
    ports:
      - "3000:3000"

YAML 1.1 vs YAML 1.2

YAML 1.1 (большинство парсеров):

  • 22 булевых значения (yes/no/on/off/y/n)
  • Восьмеричные числа (ведущий ноль)
  • Шестидесятеричные числа (60:30 = 3630 секунд)
  • Более снисходительный, больше сюрпризов

YAML 1.2 (рекомендуется):

  • Только true/false (регистронезависимо)
  • Нет восьмеричных чисел
  • Надмножество JSON (валидный JSON это валидный YAML)
  • Более предсказуемый, меньше сюрпризов

Проверьте версию YAML вашего парсера. Многие инструменты всё ещё используют YAML 1.1, поэтому безопаснее заключать в кавычки неоднозначные значения.

Тестирование конвертаций YAML

Используйте JSON ↔ YAML конвертер для тестирования того, как ваш YAML конвертируется в JSON. Это помогает идентифицировать нежелательные преобразования типов до того, как они вызовут баги.

Рабочий процесс тестирования

  1. Вставьте ваш YAML в YAML конвертер
  2. Конвертируйте в JSON
  3. Проверьте, соответствуют ли типы ожиданиям
  4. Добавьте кавычки где необходимо
  5. Повторно конвертируйте и проверьте

Краткая справка: Правила вывода типов

YAML значениеИнтерпретируется какJSON результатКак форсировать строку
yesБулевоtrue"yes"
noБулевоfalse"no"
onБулевоtrue"on"
offБулевоfalse"off"
NOБулево (YAML 1.1)false"NO"
nullNullnull"null"
~Nullnull"~"
007Восьмеричное число (YAML 1.1)7"007"
0x10Hex число16"0x10"
1.23e5Научная нотация123000"1.23e5"
.infБесконечностьInfinity (специальное)".inf"

Лучшие практики

  • Заключайте в кавычки неоднозначные значения - Когда сомневаетесь, добавьте кавычки
  • Тестируйте конвертации - Используйте YAML↔JSON конвертер для проверки типов
  • Предпочитайте YAML 1.2 - Используйте парсеры, поддерживающие YAML 1.2, когда возможно
  • Документируйте вашу схему - Указывайте ожидаемые типы в комментариях или документации
  • Используйте линтеры - YAML линтеры могут предупреждать о неявных преобразованиях
  • Будьте явными - Пишите true/false вместо yes/no

Резюме

Неявное преобразование типов в YAML может вызывать неожиданные баги:

  • yes/no/on/off становятся булевыми
  • null/~/пустые значения становятся null
  • Ведущие нули запускают восьмеричное преобразование
  • Коды стран типа NO становятся false
  • Префикс 0x создаёт hex числа

Решение: Заключайте значения в кавычки, когда вам нужны явные строки. Тестируйте ваши YAML конвертации с JSON ↔ YAML конвертером, чтобы поймать проблемы с типами на ранней стадии.

При работе с конфигурационными файлами предпочитайте явное заключение в кавычки, а не полагайтесь на вывод типов. Это делает ваши конфигурационные файлы более предсказуемыми и предотвращает сложные для отладки баги приведения типов.


Нужно безопасно конвертировать между JSON и YAML? Используйте JSON ↔ YAML конвертер для тестирования конвертаций и проверки типов. Также проверьте JSON форматтер для валидации структуры JSON после конвертации.