Как правильно прочитать XML из ответа HTTP-сервиса и разобрать формат ЗначениеВСтрокуВнутр?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При интеграции 1С с внешними системами или другими базами 1С через HTTP-сервисы, мы часто получаем данные в виде XML. Однако процесс получения и обработки этих данных может вызвать трудности у специалистов, впервые столкнувшихся с такой задачей. В данной статье мы подробно разберем, как обрабатывать строковое тело ответа без создания временных файлов, и что делать, если вместо ожидаемого XML приходит строка вида {"#",...}.

Получение данных из HTTP-сервиса

Стандартный сценарий работы с HTTP-запросом в 1С выглядит следующим образом: мы создаем соединение, формируем запрос, отправляем его и получаем ответ. Рассмотрим базовый код получения тела ответа в виде строки.


HTTPЗапрос = Новый HTTPЗапрос(Ресурс);
HTTPЗапрос.Заголовки.Вставить("Content-type", "application/xml"); 
Результат = HTTPСоединение.Получить(HTTPЗапрос);
СтрокаОтветаСервера = Результат.ПолучитьТелоКакСтроку();

На этом этапе мы имеем переменную СтрокаОтветаСервера, содержащую XML. Основной вопрос, который возникает: как передать эту строку в объект ЧтениеXML? Стандартный метод ОткрытьФайл() требует путь к файлу на диске. Создавать временный файл, записывать туда строку, читать его и затем удалять — это рабочее, но неэффективное решение, создающее лишнюю нагрузку на дисковую подсистему.

Способ 1: Чтение XML из строки без файла

Платформа 1С предоставляет механизмы для чтения XML напрямую из оперативной памяти. Самый простой и распространенный способ — использование метода УстановитьСтроку().

Давайте посмотрим, как это реализуется на практике:


ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(СтрокаОтветаСервера);

// Далее работаем как обычно
Пока ЧтениеXML.Прочитать() Цикл
    // Обработка узлов XML
КонецЦикла;

Этот метод идеально подходит для небольших и средних объемов данных. Он позволяет парсеру работать с переменной типа Строка как с потоком данных.

Способ 2: Использование потоков (Рекомендуется для мобильной платформы и больших данных)

В некоторых случаях, особенно на мобильных платформах или при работе с очень большими ответами, метод УстановитьСтроку может вызывать ошибки или нехватку памяти. Более современным подходом является использование потоков.

Вместо получения тела как строки, мы можем сразу получить поток, либо открыть поток из строки:


// Вариант 1: Если строка уже есть
ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.ОткрытьПоток(СтрокаОтветаСервера); // Доступно не во всех версиях платформы напрямую из строки, иногда требуется преобразование

// Вариант 2: Получение потока сразу из ответа (наиболее оптимально для памяти)
// Требует использования метода ПолучитьТелоКакПоток() у объекта HTTPОтвет, если версия платформы позволяет

Использование метода ОткрытьПоток позволяет избежать дублирования больших данных в памяти.

Способ 3: Разбор через Фабрику XDTO

Если структура XML известна заранее или вы хотите получить объектную модель данных, вместо последовательного чтения можно использовать ФабрикаXDTO. Это позволяет "развернуть" весь XML в дерево объектов одной командой.


ЧтениеXML = Новый ЧтениеXML;
ЧтениеXML.УстановитьСтроку(СтрокаОтветаСервера);

// Читаем весь документ сразу
ДанныеXDTO = ФабрикаXDTO.ПрочитатьXML(ЧтениеXML);

// Теперь можно обращаться к полям через точку
Результат = ДанныеXDTO.КакойТоУзел.Значение;

Этот способ значительно упрощает код, но требует, чтобы XML был валидным. При проектировании систем обмена часто также применяется ЛеМурр — конструктор API для обмена базы 1С 8.3 с другими системами в формате JSON.

Проблема формата: Ответ в виде {"#",...}

Иногда при интеграции между двумя базами 1С разработчики сталкиваются с ситуацией, когда сервер возвращает не чистый XML, а странную структуру, начинающуюся с фигурных скобок. Рассмотрим пример такого ответа:


{"#",4238019d-7e49-4fc9-91db-b6b951d5cf8e,
{3,
{
{"S","Результат"},
{"B",1}
},
...

Если попытаться "скормить" эту строку объекту ЧтениеXML, вы получите ошибку, так как это не XML. Это внутренний формат сериализации 1С, полученный в результате работы функции ЗначениеВСтрокуВнутр().

Почему так происходит?
Скорее всего, HTTP-сервис на стороне отправителя просто сериализовал какой-то объект (например, Структуру или Соответствие), содержащий внутри себя строку с XML, и вернул это "как есть".

Как это обработать?
Нам необходимо выполнить обратное преобразование — десериализацию, чтобы получить объект 1С, и уже из него извлечь нужный нам XML.

Разберем алгоритм решения:

  1. Используем функцию ЗначениеИзСтрокиВнутр() для преобразования строки ответа в объект.
  2. Анализируем полученный объект (обычно это Структура) и находим в нем ключ, содержащий XML.
  3. Передаем найденную строку XML в ЧтениеXML.

Пример кода для такой ситуации:


// 1. Десериализуем ответ сервиса
Попытка
    ПолученныйОбъект = ЗначениеИзСтрокиВнутр(СтрокаОтветаСервера);
Исключение
    // Обработка ошибки, если формат не распознан
    Возврат;
КонецПопытки;

// 2. Извлекаем XML. Допустим, мы увидели в отладчике, что XML лежит в свойстве "Данные"
Если ТипЗнч(ПолученныйОбъект) = Тип("Структура") И ПолученныйОбъект.Свойство("Данные") Тогда
    ЧистаяСтрокаXML = ПолученныйОбъект.Данные;
    
    // 3. Читаем извлеченный XML
    ЧтениеXML = Новый ЧтениеXML;
    ЧтениеXML.УстановитьСтроку(ЧистаяСтрокаXML);
    
    // Далее стандартный парсинг или XDTO
    // ...
КонецЕсли;

Важно понимать, что использование ЗначениеВСтрокуВнутр и ЗначениеИзСтрокиВнутр для обмена данными считается плохой практикой (за исключением специфических внутренних задач), так как формат жестко привязан к платформе 1С. Однако, если сторонний сервис отдает данные именно так, у нас нет выбора, кроме как парсить их указанным способом.

Частая ошибка: "Document is empty"

Если при попытке прочитать XML вы получаете ошибку "Document is empty" (Документ пуст), хотя строка визуально содержит данные, проверьте следующие моменты:

Подводя итог: для чтения XML из памяти используйте ЧтениеXML.УстановитьСтроку(). Если же вы видите формат с фигурными скобками, сначала восстановите объект через ЗначениеИзСтрокиВнутр().

← На главную