Пустые значения на выходе из 1С: когда отдавать null, когда пустую строку, а когда вообще не передавать поле
Содержание
Чтобы передать NULL из 1С во внешнюю систему, значение нужно явно превратить в тот вид, который понимает приёмник. Для JSON это Неопределено перед вызовом ЗаписатьJSON() — оно записывается как null. Для SQL — литерал NULL в тексте запроса или параметр ADO со значением Null. Для SOAP — xsi:nil="true" у элемента со свойством nillable или отсутствие элемента, если он необязательный.
Главная ловушка в другом. В 1С «пусто» почти никогда не равно NULL: пустая строка, ноль, пустая дата 01.01.0001 и пустая ссылка — это полноценные значения своих типов. Если не перевести их в null вручную, внешняя система получит "", 0 или дату первого года нашей эры.
Чем Null в 1С отличается от пустого значения
В 1С три разных понятия «ничего нет», и путать их нельзя:
- 🔹
Null— значение типа Null. Появляется почти только в результатах запросов: поле из левого соединения, у которого нет пары, или реквизит, которого нет у части объектов составного типа. - 🔹
Неопределено— «значение не задано». Так выглядит незаполненная переменная, отсутствующий ключ вСоответствии, пустой реквизит составного типа. - 🔹 Пустое значение типа —
"",0,Ложь,Дата(1, 1, 1),Справочники.Контрагенты.ПустаяСсылка(). Реквизит объекта с одним типом хранит именно его, а не Null.
Отсюда простое правило: реквизиты справочников и документов никогда не возвращают Null сами по себе. Null вы получите из запроса, а Неопределено — из составного типа или пустой переменной.
| Что в 1С | Где встречается | Что обычно ждёт приёмник |
|---|---|---|
Null | результат запроса, левое соединение | null / NULL |
Неопределено | составной тип, пустая переменная | null или поле опускают |
"" | реквизит типа Строка | "" или null — по контракту |
Дата(1, 1, 1) | незаполненная дата | почти всегда null |
ПустаяСсылка() | незаполненная ссылка | null, а не пустой GUID |
Проверка «есть ли значение» для всех вариантов сразу — ЗначениеЗаполнено(Значение). Для Null, Неопределено, пустой строки, нуля, пустой даты и пустой ссылки она вернёт Ложь.
Но ноль и Ложь часто являются осмысленными данными. Скидка 0% — это не «скидки нет». Поэтому применяйте ЗначениеЗаполнено() к числам и булевым только там, где ноль действительно означает «не задано».
Откуда берутся Null в запросе
Типичный источник — левое соединение со срезом цен:
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| Номенклатура.Ссылка КАК Номенклатура,
| Цены.Цена КАК Цена,
| ЕСТЬNULL(Цены.Цена, 0) КАК ЦенаИлиНоль
|ИЗ
| Справочник.Номенклатура КАК Номенклатура
| ЛЕВОЕ СОЕДИНЕНИЕ РегистрСведений.ЦеныНоменклатуры.СрезПоследних КАК Цены
| ПО Номенклатура.Ссылка = Цены.Номенклатура";
Для позиции без цены поле Цена будет Null, а ЦенаИлиНоль — 0. Если внешней системе важно отличать «цена 0» от «цены нет», не прячьте Null за ЕСТЬNULL — оставьте его и передайте как null.
Передача null в JSON и через HTTP-сервисы
Встроенная функция ЗаписатьJSON() записывает Неопределено как null. Поэтому надёжная схема такая: собрать данные в Структуру или Соответствие, а все «пустые» значения заранее заменить на Неопределено.
Шаг 1. Функция приведения значения
Функция ЗначениеДляJSON(Значение)
Если Значение = Null Или Значение = Неопределено Тогда
Возврат Неопределено;
КонецЕсли;
ТипЗначения = ТипЗнч(Значение);
Если ТипЗначения = Тип("Дата") Тогда
Если Значение = Дата(1, 1, 1) Тогда
Возврат Неопределено;
КонецЕсли;
Возврат Формат(Значение, "ДФ='yyyy-MM-ddTHH:mm:ss'");
КонецЕсли;
Если ОбщегоНазначения.ЭтоСсылка(ТипЗначения) Тогда
Если Не ЗначениеЗаполнено(Значение) Тогда
Возврат Неопределено;
КонецЕсли;
Возврат Строка(Значение.УникальныйИдентификатор());
КонецЕсли;
Возврат Значение;
КонецФункции
ОбщегоНазначения.ЭтоСсылка() — функция Библиотеки стандартных подсистем, она есть во всех типовых конфигурациях. Строки и числа функция возвращает как есть: пустая строка останется "". Если по контракту API пустая строка должна уходить как null, добавьте ветку для типа Строка.
Шаг 2. Сборка и запись
Данные = Новый Структура;
Данные.Вставить("inn", ЗначениеДляJSON(Выборка.ИНН));
Данные.Вставить("price", ЗначениеДляJSON(Выборка.Цена));
Данные.Вставить("contract_date", ЗначениеДляJSON(Выборка.ДатаДоговора));
Запись = Новый ЗаписьJSON;
Запись.УстановитьСтроку();
ЗаписатьJSON(Запись, Данные);
ТекстJSON = Запись.Закрыть();
Для позиции без цены и без договора получится {"inn": "7701234567", "price": null, "contract_date": null}.
Шаг 3. Отправка по HTTP
Соединение = Новый HTTPСоединение("api.example.ru", 443, , , , 30,
Новый ЗащищенноеСоединениеOpenSSL);
Запрос = Новый HTTPЗапрос("/v1/items");
Запрос.Заголовки.Вставить("Content-Type", "application/json; charset=utf-8");
Запрос.УстановитьТелоИзСтроки(ТекстJSON, КодировкаТекста.UTF8,
ИспользованиеByteOrderMark.НеИспользовать);
Ответ = Соединение.ОтправитьДляОбработки(Запрос);
ИспользованиеByteOrderMark.НеИспользовать убирает BOM в начале тела. Многие API на BOM отвечают ошибкой разбора JSON ещё до того, как дойдут до ваших null.
null, пустое поле или отсутствие поля
У REST-API часто три разных смысла:
- ✅ поля нет — «не меняй это значение» (частичное обновление, PATCH);
- ✅
"field": null— «очисти значение»; - ✅
"field": ""— «запиши пустую строку».
Если API так различает случаи, не вставляйте ключ в Структуру вовсе, когда значение менять не нужно. Неопределено в структуре всё равно превратится в null и сотрёт данные на стороне приёмника.
Параметры в адресе запроса
В строке запроса (?inn=...&kpp=...) понятия null нет. Есть только «параметр отсутствует» и «параметр пустой» (kpp=). Если значение не задано — не добавляйте параметр в адрес. Если API требует явный признак, договоритесь о нём с разработчиками приёмника, а не отправляйте строку null: её прочитают как текст из четырёх букв.
NULL в прямых SQL-запросах через ADO и ODBC
Запрос языка 1С к своей базе SQL здесь ни при чём: речь о записи во внешнюю базу — MS SQL Server, PostgreSQL, MySQL. Из 1С это обычно делают через COM-объект ADODB.Connection (только в Windows) или через внешние источники данных.
Способ 1. Параметры команды (рекомендуемый)
Соединение = Новый COMОбъект("ADODB.Connection");
Соединение.Open(СтрокаПодключения);
Команда = Новый COMОбъект("ADODB.Command");
Команда.ActiveConnection = Соединение;
Команда.CommandText = "UPDATE dbo.Clients SET Phone = ? WHERE Inn = ?";
Телефон = ?(ЗначениеЗаполнено(Выборка.Телефон), Выборка.Телефон, Null);
// 202 — adVarWChar, 1 — adParamInput
Команда.Parameters.Append(Команда.CreateParameter("Phone", 202, 1, 50, Телефон));
Команда.Parameters.Append(Команда.CreateParameter("Inn", 202, 1, 12, Выборка.ИНН));
Команда.Execute();
Соединение.Close();
Значение Null из 1С при передаче в COM становится вариантом VT_NULL, и драйвер записывает в поле NULL. Неопределено передаётся как VT_EMPTY — с ним драйверы ведут себя по-разному, поэтому для пустых параметров используйте именно Null.
Способ 2. Сборка текста запроса
Иногда без склейки строки не обойтись — например, при пакетной вставке тысяч строк одним запросом. Тогда нужна функция-литерал:
Функция ЛитералSQL(Значение)
Если Значение = Null Или Значение = Неопределено Тогда
Возврат "NULL";
ИначеЕсли ТипЗнч(Значение) = Тип("Число") Тогда
Возврат Формат(Значение, "ЧРД=.; ЧН=0; ЧГ=0");
ИначеЕсли ТипЗнч(Значение) = Тип("Дата") Тогда
Если Значение = Дата(1, 1, 1) Тогда
Возврат "NULL";
КонецЕсли;
Возврат "'" + Формат(Значение, "ДФ='yyyyMMdd HH:mm:ss'") + "'";
ИначеЕсли ТипЗнч(Значение) = Тип("Булево") Тогда
Возврат ?(Значение, "1", "0");
Иначе
Возврат "'" + СтрЗаменить(Строка(Значение), "'", "''") + "'";
КонецЕсли;
КонецФункции
Два места, где ошибаются чаще всего:
- ⚠️
ЧН=0в формате числа. Без негоФормат(0, "ЧГ=0")вернёт пустую строку, и в запросе окажетсяVALUES (, 'abc')— синтаксическая ошибка. - ⚠️
ЧРД=.— точка как десятичный разделитель. Русская запятая в SQL разобьёт число на два значения.
Склейка текста из пользовательских данных открывает дорогу SQL-инъекциям. Удвоение апострофа закрывает самый частый случай, но параметры команды надёжнее. Тексты, пришедшие от внешних систем или из форм, вставляйте только через параметры.
Пустая дата и MS SQL
Тип datetime в MS SQL Server хранит даты начиная с 1753 года. Пустая дата 1С 01.01.0001 в него не помещается, и запрос падает с ошибкой выхода значения за допустимый диапазон. Поэтому пустую дату всегда переводите в NULL — функция выше так и делает. Тип datetime2 и PostgreSQL первый год примут, но приёмник получит «мусорную» дату вместо отсутствия значения.
Внешние источники данных
Если внешняя таблица подключена через объект конфигурации «Внешние источники данных», NULL из неё приходит в запросе 1С обычным Null. Обрабатывайте его так же, как поле из левого соединения: ЕСТЬNULL() в тексте запроса или проверка Значение = Null в коде. Чтобы поле внешней таблицы вообще могло хранить и возвращать Null, у него в свойствах должна быть включена возможность пустого значения — проверьте это в конфигураторе, если при чтении получаете ошибки преобразования.
Пустые значения в SOAP и веб-сервисах (WSDL, XDTO)
В XML у «пустого» тоже несколько форм, и выбирает её не 1С, а схема из WSDL:
| В схеме | Как передать «нет значения» | Что увидит приёмник |
|---|---|---|
minOccurs="0" | не заполнять свойство | элемента нет в XML |
nillable="true" | присвоить Неопределено | <phone xsi:nil="true"/> |
| ни того, ни другого | пустое значение нельзя | ошибка проверки по схеме |
При работе через WSПрокси и объекты XDTO проверить и сбросить свойство помогают методы объекта XDTO Установлено("ИмяСвойства") и Сбросить("ИмяСвойства"). Сброшенное необязательное свойство не попадёт в XML совсем.
Если веб-сервис опубликован в вашей конфигурации, nillable задаётся в пакете XDTO: у свойства есть флаг «Возможно пустое». Без него сторонняя система не сможет прислать вам xsi:nil, а ваш сервис не сможет его отдать — получите ошибку проверки значения на соответствие типу.
Перед разработкой откройте WSDL приёмника и найдите у нужных элементов атрибуты minOccurs и nillable. Пять минут чтения схемы снимают половину вопросов на тестировании.
Как обработать входящий null в 1С
Обратная задача — внешняя система прислала JSON с null, и его надо разобрать.
Чтение JSON
Функция ПрочитатьJSON() превращает null в Неопределено. Отличить «поле пришло со значением null» от «поля нет вовсе» можно через метод структуры Свойство():
Чтение = Новый ЧтениеJSON;
Чтение.УстановитьСтроку(ТекстОтвета);
Данные = ПрочитатьJSON(Чтение);
Чтение.Закрыть();
Телефон = Неопределено;
Если Не Данные.Свойство("phone", Телефон) Тогда
// поля нет — оставляем реквизит как есть
ИначеЕсли Телефон = Неопределено Тогда
// пришёл null — очищаем
Объект.Телефон = "";
Иначе
Объект.Телефон = Телефон;
КонецЕсли;
Если ключи JSON не годятся в имена свойств структуры (дефис, цифра в начале), читайте в соответствие: ПрочитатьJSON(Чтение, Истина). У Соответствия.Получить() нет различия между отсутствующим ключом и null — оба вернут Неопределено. Если различие важно, обходите соответствие циклом Для Каждого и собирайте список пришедших ключей.
Запись в реквизиты
Не присваивайте Неопределено реквизиту напрямую в расчёте на «как-нибудь сработает». Явно задавайте пустое значение нужного типа:
- 🔸 строка —
""; - 🔸 дата —
Дата(1, 1, 1); - 🔸 число —
0, если по смыслу нет значения, или отдельный признак «не задано»; - 🔸 ссылка —
ПустаяСсылка()соответствующего справочника.
Чтение из ADO
Поле записи Recordset со значением NULL в базе возвращается в 1С как Null. Перед арифметикой и обращением через точку проверяйте его: ?(Поле.Value = Null, 0, Поле.Value).