Некоторые системы принимают выгрузки в XML, хотя внутри n8n данные обычно приходят как JSON. Превратим три заказа в один XML-документ штатным узлом XML, проверим обратным чтением и создадим файл для скачивания. Теги вручную склеивать не будем.
Результат инструкции
Получим orders-demo.xml с корнем orders и тремя элементами order. В каждом будут id, name и quantity. Номера 001, 002 и 003 останутся строками, количество 0 не исчезнет, амперсанд в названии товара не сломает документ.
Это учебный формат, не готовый обмен с 1С или маркетплейсом. Схема проверяет структуру и совпадение данных, но не XSD, бизнес-правила или приём файла чужим API. Отправки, загрузки в рабочую систему и AI-вызовов здесь нет.
Что понадобится
- Отдельный новый workflow в n8n. Проверено на n8n 2.42.4, XML 1, Convert to File 1.1 и Code 2.
- Учебные заказы из статьи. Внешние аккаунты, ключи и реальные клиентские данные не нужны.
- Текстовый редактор или браузер для просмотра XML. Файл содержит UTF-8, а не Windows-1251.
Как установить n8n локально через Docker
Шаг 1. Импортируйте готовую схему
Скачать n8n-json-to-xml-export.json
Создайте новый workflow и выберите Import from File. Загрузите JSON и сохраните его под отдельным названием. Публиковать workflow не нужно: он запускается кнопкой вручную. Восемь узлов идут последовательно: Manual Trigger → Sample Orders → Prepare XML → Build XML → Read XML → Verify XML → Make File → Check File.
Sample Orders, Prepare XML, Verify XML и Check File — Code, язык JavaScript, режим Run Once for All Items. Два XML-узла создают документ и читают его обратно. Make File превращает проверенный текст в binary. Названия узлов оставьте как в схеме: код использует ссылки на них.
Шаг 2. Проверьте исходные заказы
Откройте Sample Orders. При ручной сборке вставьте:
return [
{
"order_id": "001",
"product": "Чай & кофе",
"quantity": 2
},
{
"order_id": "002",
"product": "Блокнот",
"quantity": 0
},
{
"order_id": "003",
"product": "Лампа \"Старт\"",
"quantity": 1
}
].map(json => ({ json }));
На выходе должны быть три отдельных items, не один объект с массивом внутри. Номер передаётся строкой в кавычках. Если сделать из 001 число, нули пропадут до преобразования в XML, и узел не сможет их восстановить.
У второго товара quantity равно 0. Это корректное значение нашего примера. Пустая строка, null и неизвестное количество не равны нулю: схема отклонит их, а не заменит автоматически.
Шаг 3. Соберите один объект для экспорта
В Prepare XML вставьте следующий код:
const items = $input.all();
if (items.length < 1 || items.length > 20) throw new Error('rows_limit');
const ids = new Set();
const orders = items.map(({ json: row }) => {
if (Object.keys(row).sort().join('|') !== 'order_id|product|quantity') throw new Error('fields_invalid');
if (typeof row.order_id !== 'string' || !/^\d{3}$/.test(row.order_id) || ids.has(row.order_id)) {
throw new Error('id_invalid_or_duplicate');
}
ids.add(row.order_id);
if (typeof row.product !== 'string' || !row.product.trim() || row.product.length > 120 ||
!row.product.isWellFormed() || /[\u0000-\u001f\u007f\ufffe\uffff]/.test(row.product)) {
throw new Error('product_invalid');
}
if (!Number.isSafeInteger(row.quantity) || row.quantity < 0) throw new Error('quantity_invalid');
return { id: row.order_id, name: row.product, quantity: String(row.quantity) };
});
return [{ json: { order: orders }, pairedItem: items.map((_, item) => ({ item })) }];
Проверка принимает от одной до двадцати записей. У каждой должны быть ровно order_id, product и quantity; номера уникальные и трёхзначные, количество — неотрицательное безопасное целое число. Название товара ограничено 120 символами, недопустимые управляющие символы и повреждённые Unicode-строки отклоняются. Это учебный контракт данных, не универсальные ограничения XML.
После проверки получается один item с полем order, внутри которого массив заказов. Поля переименованы в id, name и quantity, а числа явно переведены в текст. У XML нет автоматической памяти о JSON-типах: после обратного чтения количество будет строкой. Сравнивать его нужно с тем же строковым представлением, не менять номера на числа.
Значения не нужно заранее заменять на & или <. Это сделает XML-сериализатор. Предварительное экранирование изменит буквальный текст: вместо амперсанда получатель может увидеть запись &. Названия тегов заданы в коде, а не берутся из пользовательских ключей.
Шаг 4. Настройте JSON to XML
В Build XML выберите Mode: JSON to XML. Укажите:
| Настройка | Значение | Для чего |
|---|---|---|
| Property Name | xml | Имя выходного JSON-поля с XML-строкой |
| Root Name | orders | Один общий корень документа |
| Headless | Выключено | Сохранить XML-декларацию |
| Cdata | Выключено | Обычное экранирование значений сериализатором |
Attribute Key оставьте $, Character Key — _, Allow Surrogate Chars — выключено. В нашем объекте нет атрибутов или смешанного содержимого, поэтому эти специальные ключи не используются.
В проверенном n8n поле Property Name в режиме JSON to XML задаёт, куда записать результат. Узел сериализует весь входной item.json, а не только поле с таким именем. Поэтому вход здесь — объект с order, а выход — объект с xml. Не оборачивайте вход дополнительно в xml: это изменит структуру документа.
На выходе Build XML один item, внутри xml — такой документ:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<orders>
<order>
<id>001</id>
<name>Чай & кофе</name>
<quantity>2</quantity>
</order>
<order>
<id>002</id>
<name>Блокнот</name>
<quantity>0</quantity>
</order>
<order>
<id>003</id>
<name>Лампа "Старт"</name>
<quantity>1</quantity>
</order>
</orders>
Чай & кофе записан в файле как Чай & кофе. Это правильное представление амперсанда в XML, не ошибка кодировки. Кавычки внутри текста элемента допускаются как есть. Символы < и > в значениях сериализатор тоже обрабатывает; не подставляйте их в строку с тегами вручную.
Шаг 5. Настройте обратное чтение
В Read XML выберите Mode: XML to JSON и следующие параметры:
| Настройка | Значение | Для чего |
|---|---|---|
| Property Name | xml | Прочитать строку из Build XML |
| Explicit Array | Включено | Одинаковая структура даже для одного заказа |
| Explicit Root | Включено | Сохранить уровень orders |
| Merge Attributes | Выключено | Не смешивать атрибуты с элементами |
Ignore Attributes, Normalize, Normalize Tags и Trim оставьте выключенными. Здесь читается только что созданный учебный документ, а не произвольный файл извне. При Explicit Array текстовые элементы тоже становятся массивами: id выглядит как ["001"], quantity — как ["2"]. Это ожидаемый результат, не лишние дубли.
Если выключить Explicit Array, структура одного и нескольких повторяющихся элементов может отличаться. Проверка ниже намеренно требует стабильный вариант. Для чужих XML с пространствами имён, атрибутами и другой вложенностью нужен отдельный разбор по спецификации получателя.
Шаг 6. Сверьте данные с источником
В Verify XML вставьте:
const parsed = $input.first().json;
const source = $('Sample Orders').all().map(item => item.json);
const orders = parsed.orders?.order;
if (Object.keys(parsed).join('|') !== 'orders' || !Array.isArray(orders) || orders.length !== source.length) {
throw new Error('orders_structure_mismatch');
}
for (let i = 0; i < source.length; i++) {
const actual = orders[i];
const expected = { id: [source[i].order_id], name: [source[i].product], quantity: [String(source[i].quantity)] };
if (JSON.stringify(actual) !== JSON.stringify(expected)) throw new Error('order_content_mismatch');
}
const xml = $('Build XML').first().json.xml;
if (typeof xml !== 'string' || !xml.startsWith('<?xml version="1.0" encoding="UTF-8"')) {
throw new Error('xml_declaration_required');
}
return [{ json: { xml, count: source.length, verified: true } }];
Код проверяет корень orders, число заказов и все три значения каждого заказа в исходном порядке. Источник сравнения — Sample Orders, а не промежуточный уже преобразованный объект. Поэтому пропуск поля или изменение значения должны остановить схему.
Успешное обратное чтение не заменяет проверку XSD. Наш файл может быть корректным XML и при этом не подходить конкретному API. Здесь verified означает только совпадение с учебным контрактом и источником.
Шаг 7. Создайте скачиваемый файл
В Make File выберите Convert to File → Convert to Text File. Заполните поля:
- Text Input Field: xml.
- Put Output File in Field: export.
- В Options укажите File Name: orders-demo.xml и Encoding: utf8.
- Add Byte Order Mark (BOM): выключено.
Имя export — это ключ в binary, а не путь на диске сервера. Имя orders-demo.xml — имя скачиваемого файла. Расширение не перекодирует текст: Encoding должно соответствовать UTF-8 в XML-декларации.
У Convert to Text File в проверенной версии нет поля MIME Type: он помечает binary как text/plain, даже если файл назван .xml. Байты при этом остаются корректным XML. Мы проверили это отдельно. Если позже API потребует application/xml, Content-Type нужно явно согласовать в узле отправки; простое переименование файла его не меняет.
Шаг 8. Проверьте байты файла
В Check File вставьте:
const items = $input.all();
if (items.length !== 1 || Object.keys(items[0].binary ?? {}).join('|') !== 'export') {
throw new Error('one_export_file_required');
}
const item = items[0];
if (item.binary.export.fileName !== 'orders-demo.xml') throw new Error('file_name_invalid');
const bytes = await this.helpers.getBinaryDataBuffer(0, 'export');
const expected = Buffer.from($('Verify XML').first().json.xml, 'utf8');
if (!bytes.equals(expected)) throw new Error('file_bytes_mismatch');
return [{ json: {
file_name: 'orders-demo.xml', count: $('Verify XML').first().json.count,
bytes: bytes.length, verified: true, action_performed: false
}, binary: item.binary }];
getBinaryDataBuffer читает настоящий файл, в том числе при хранении binary на диске. Служебное поле binary.export.data не следует считать содержимым XML. Код требует один файл с ожидаемым именем и побайтовое совпадение с проверенной XML-строкой в UTF-8.
Make File не сохраняет прежние JSON-поля на выходе. Поэтому Check File берёт проверенный XML и число записей по ссылке на Verify XML, а сам binary передаёт дальше без изменения. Здесь не создаётся новый файл ручной склейкой или Base64-кодированием.
Шаг 9. Запустите workflow и проверьте ответ
Выполните всю цепочку от Manual Trigger. Все восемь узлов должны завершиться успешно. В JSON последнего узла ожидаются:
{
"file_name": "orders-demo.xml",
"count": 3,
"bytes": 381,
"verified": true,
"action_performed": false
}
В учебном образце файл занимает 381 байт. Это размер конкретных данных и форматирования, не требование для ваших будущих выгрузок. Главное — verified: true, count: 3 и наличие binary.export. action_performed: false означает, что внешней отправки не было.
В Check File откройте Binary, выберите export и скачайте файл. В текстовом редакторе проверьте декларацию UTF-8, один корень orders, три order, номера 001–003 и нулевое количество у блокнота. Для сравнения доступен точный образец, полученный запуском этой схемы:
Шаг 10. Проверьте спецсимволы и неверный вход
В Sample Orders временно замените product первого заказа на <b>Чай</b>. Запустите всю схему. В XML появится <b>Чай</b>, а Read XML вернёт буквальную строку <b>Чай</b>. Это не вложенный тег b, а текст товара. Проверка должна пройти.
Затем задайте quantity: -1. Prepare XML должен завершиться ошибкой quantity_invalid; файл не создаётся. Верните исходный пример и повторите запуск. Дополнительно можно оставить один заказ: при Explicit Array схема по-прежнему должна пройти.
Если Sample Orders вернёт ноль items, последующие узлы не запустятся и файла не будет. Не считайте такой запуск успешной пустой выгрузкой. Для пустого документа нужен отдельный заранее определённый сценарий, которого здесь нет.
Если результат отличается
| Симптом | Что проверить | Как исправить |
|---|---|---|
| Получилось несколько XML-документов | На входе Build XML несколько items | Собрать один item в Prepare XML |
| Лишний уровень вложенности | Вход обёрнут в поле xml | Передавать объект с массивом order |
| orders_structure_mismatch | Root Name и Explicit Root | Вернуть orders и сохранение корня |
| order_content_mismatch | Explicit Array, значения и порядок | Сверить Read XML с Sample Orders |
| file_bytes_mismatch | Encoding и Add BOM | Вернуть utf8 без BOM |
Как подготовить обмен с реальной системой
Сначала получите описание формата от получателя: точный корень, имена и порядок полей, обязательные атрибуты, пространства имён и правила для пустых значений. Учебные orders и order не являются стандартом 1С, CommerceML или любого маркетплейса. Менять их нужно вместе с проверкой структуры, а не только с названием файла.
Для отправки используйте отдельный тестовый контур и согласованный Content-Type. До подтверждения успешного приёма не отмечайте заказ выгруженным. Этот workflow ничего не отправляет и не проверяет чужие credentials. Источник с рабочими или персональными данными подключайте только с разрешённым доступом и настройками хранения executions.
Обратная задача: XML в JSON в n8n
Как отправить файл через HTTP Request
Что проверено редакцией
18 октября 2026 года точная публичная схема проверена на n8n 2.42.4 в отдельном Docker-экземпляре с binary на диске. Все 38 сценариев прошли: три, один и двадцать заказов, обратный порядок, нули, Unicode, пробелы, кавычки, амперсанд, буквальные теги и HTML-сущности; Cdata как отдельная диагностика; неверные типы, номера, дубли, пустой и слишком длинный текст, управляющие символы, непарный суррогат, отрицательные, дробные и небезопасные числа, пропущенные и лишние поля; неверный корень, Explicit Root, Explicit Array, Property Name, Headless, источник файла, имя, binary-поле, BOM и UTF-16. Пустой источник не создал файл. XML строился и читался штатным узлом XML, файл создавался Convert to File. Образец UTF-8 на 381 байт дополнительно разобран независимым Python ElementTree: все три заказа совпали. Подтверждён MIME text/plain у Convert to Text File, это отмечено в статье. Контейнер остановлен, существующие workflows не менялись. Клики редактора, ручное скачивание из n8n, Cloud, XSD, 1С и внешние API не тестировались. Обложка — AI-иллюстрация.
Частые вопросы
Property Name выбирает поле JSON для преобразования?
В режиме JSON to XML в проверенной версии это имя выходного поля с XML-строкой. Сериализуется весь входной item.json. В режиме XML to JSON это, напротив, имя входного поля с XML.
Почему количество после чтения XML стало строкой?
В XML текстовый элемент сам не хранит JSON-тип Number. В нашей схеме число заранее превращается в строку, после чтения сравнивается с ней же. Номер заказа числом делать не нужно.
Нужно вручную заменять амперсанды и угловые скобки?
Нет. При Cdata: выключено XML-сериализатор экранирует значения. Входите обычным текстом, иначе можно получить двойное экранирование.
Почему файл .xml отмечен как text/plain?
Convert to Text File в проверенной версии использует этот MIME. Расширение и байты XML от этого не меняются. Для отправки в API согласуйте Content-Type отдельно.
Можно загрузить этот файл в 1С без изменений?
Не следует это предполагать. Это учебная структура, не реализация формата обмена 1С. Сначала нужны спецификация получателя, проверка обязательных полей и тестовый импорт.