Пустые значения на выходе из 1С: когда отдавать null, когда пустую строку, а когда вообще не передавать поле

· 10 мин

Содержание

Чтобы передать 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).

Ошибки при передаче NULL и как их устранить

Диагностика: выберите симптом

Частые вопросы

Чем ЕСТЬNULL отличается от ВЫБОР КОГДА ... ЕСТЬ NULL?
Результат одинаковый. ЕСТЬNULL(Поле, 0) короче и читается проще; ВЫБОР нужен, когда для Null и для заполненного значения надо вернуть разные выражения.
Можно ли сравнить значение с Null через знак равенства в коде?
Да, во встроенном языке 1С проверка Значение = Null работает. В тексте запроса так нельзя: там пишут Поле ЕСТЬ NULL.
Почему Null не попадает в реквизит документа?
Реквизиты объектов с одним типом хранят пустое значение этого типа, а не Null. Null бывает в результатах запросов и во внешних данных.
Как передать null в XML без WSDL, через ЗаписьXML?
Если схемы нет, договоритесь о формате с приёмником. Обычно элемент просто не пишут или пишут пустой тег; атрибут xsi:nil имеет смысл, только когда приёмник проверяет XML по схеме.
Что выбрать для обмена с 1С на стороне приёмника — null или пустую строку?
Для дат, чисел и ссылок — null: у них нет осмысленного «пустого» значения. Для строк ориентируйтесь на контракт API; если его нет, null надёжнее отличает «не заполнено» от «заполнено пустым».