Подводные камни неявной типизации YAML: Булевы значения, Null и ведущие нули
Вы конвертируете 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. Это помогает идентифицировать нежелательные преобразования типов до того, как они вызовут баги.
Рабочий процесс тестирования
- Вставьте ваш YAML в YAML конвертер
- Конвертируйте в JSON
- Проверьте, соответствуют ли типы ожиданиям
- Добавьте кавычки где необходимо
- Повторно конвертируйте и проверьте
Краткая справка: Правила вывода типов
| YAML значение | Интерпретируется как | JSON результат | Как форсировать строку |
|---|---|---|---|
yes | Булево | true | "yes" |
no | Булево | false | "no" |
on | Булево | true | "on" |
off | Булево | false | "off" |
NO | Булево (YAML 1.1) | false | "NO" |
null | Null | null | "null" |
~ | Null | null | "~" |
007 | Восьмеричное число (YAML 1.1) | 7 | "007" |
0x10 | Hex число | 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 после конвертации.