API или выгрузка прислали XML, а следующий узел ждёт JSON. Проблема не только в скобках: ID 001 нельзя превращать в число 1, а строка false ещё не означает логическое false. Соберём в n8n цепочку, которая разберёт три заказа, проверит структуру и выдаст понятный JSON с правильными типами.
Результат инструкции
Вход — один item с текстовым полем xml. Штатный узел XML преобразует документ в объект с корнем orders, массивами дочерних элементов и отдельными атрибутами. Затем Check Orders превратит суммы в целые копейки, paid — в Boolean, сохранит строковые ID и добавит количество и итоговую сумму.
На выходе будет один item с batch_id B1, orders из трёх заказов, count 3 и total_amount_minor 199900. Схема ничего не отправляет и не записывает во внешнюю систему: action_performed false. AI-модель для разбора XML не нужна.
Что понадобится
- Работающий n8n и права создать отдельный учебный workflow. Проверено на n8n 2.42.4, XML версии 1.
- Только вымышленный XML из инструкции. HTTP-сервис, API-ключ и база не нужны.
- Входной договор примера: корень orders, атрибут batch_id и заказы с id, client, amount_minor и paid.
Установить n8n локально через Docker
Шаг 1. Импортируйте готовый workflow
Создайте новый workflow. В меню редактора выберите Import from File и загрузите JSON. Если браузер открыл текст файла, сохраните его и импортируйте. На полотне появятся пять узлов:
Manual Trigger → Sample XML → Check XML Text → XML to JSON → Check Orders
XML to JSON — штатный XML. Sample XML, Check XML Text и Check Orders — Code. Для ручной сборки используйте те же имена и последовательные соединения. Импорт не содержит credentials и ID другого workflow, остаётся неактивным. Публикация для ручного теста не требуется.
Шаг 2. Подготовьте исходный XML
В Sample XML выберите Mode: Run Once for All Items и Language: JavaScript. Удалите стандартный пример, вставьте код:
const xml = `<?xml version="1.0" encoding="UTF-8"?>
<orders batch_id="B1">
<order id="001"><client>Анна & Ко</client><amount_minor>150000</amount_minor><paid>false</paid></order>
<order id="002"><client>Борис</client><amount_minor>0</amount_minor><paid>true</paid></order>
<order id="003"><client>Альфа</client><amount_minor>49900</amount_minor><paid>false</paid></order>
</orders>`;
return [{ json: { xml } }];
Выполните источник от Manual Trigger. В Output должен быть один item с xml, внутри которого три элемента order. ID 001, 002 и 003 находятся в атрибутах, а не в дочерних тегах. Суммы заданы копейками. У заказа 002 сумма 0, и она допустима.
В имени первого клиента написано Анна & Ко. В XML амперсанд закодирован как &, после разбора должен остаться обычный символ &. Не заменяйте сущности вручную до XML-узла: можно случайно сделать корректный документ невалидным.
Шаг 3. Проверьте текст до разбора
В Check XML Text задайте Run Once for All Items, JavaScript и вставьте:
const items = $input.all();
if (items.length !== 1) throw new Error('one_xml_item_required');
const xml = items[0].json.xml;
if (typeof xml !== 'string' || !xml.trim()) throw new Error('xml_text_required');
if (Buffer.byteLength(xml, 'utf8') > 16384) throw new Error('xml_too_large');
if (xml.includes('<!DOCTYPE') || xml.includes('<!ENTITY')) {
throw new Error('xml_declarations_not_supported');
}
return [{ json: { xml } }];
Здесь принимается ровно один item, непустая строка и не более 16384 байт UTF-8. Это выбранный учебный лимит, а не максимальная возможность XML-узла. Пустота или null дают xml_text_required; превышение лимита — xml_too_large. Buffer.byteLength считает байты, поэтому размер кириллицы не следует оценивать только по длине строки.
DTD и декларации ENTITY в примере не поддерживаются: перед парсером отклоняются строки с указанными маркерами. Эта небольшая проверка не является полным аудитом безопасности XML, защитой от всех опасных входов или заменой ограничения ресурсов. Не используйте учебный workflow как публичный обработчик произвольных файлов.
Шаг 4. Настройте штатный XML
Откройте XML to JSON. В Mode выберите XML to JSON. В Property Name укажите xml обычным текстом, без выражения и фигурных скобок. Это имя входного поля, а не имя корневого XML-тега.
В Options добавьте и явно задайте пять настроек:
| Параметр | Значение | Зачем |
|---|---|---|
| Explicit Array | On | Дочерние элементы всегда массивы |
| Explicit Root | On | Сохранить корень orders |
| Ignore Attributes | Off | Не потерять id и batch_id |
| Merge Attributes | Off | Атрибуты отдельно от дочерних тегов |
| Trim | On | Убрать пробелы по краям текста |
Attribute Key оставьте $, Character Key — _. Normalize и Normalize Tags выключены: имена тегов не меняются на строчные, внутренние пробелы не нормализуются. Смысл этих настроек важнее их положения в интерфейсе; готовый файл уже содержит все значения.
В Settings всех узлов оставьте On Error: Stop Workflow. Execute Once, Always Output Data и Retry On Fail не включайте. Повтор не исправляет неверную XML-структуру.
Шаг 5. Посмотрите промежуточный JSON
Выполните XML to JSON от исходных узлов. В Output ожидается один item с orders. Внутри orders.order — массив из трёх объектов. У первого заказа объект после разбора должен выглядеть так:
{
"$": {
"id": "001"
},
"client": [
"Анна & Ко"
],
"amount_minor": [
"150000"
],
"paid": [
"false"
]
}
Символ $ здесь — обычный ключ JSON для атрибутов. ID находится в row.$.id, текст клиента — в row.client[0]. Даже amount_minor и paid пока строки внутри массивов. XML-узел в этой конфигурации не превращает их автоматически в денежную сумму и логическое значение.
Для простого текста Character Key может не появиться. Не добавляйте _ в путь только потому, что он указан в настройках: смотрите фактический JSON. Наша проверка рассчитана на простые текстовые дочерние элементы, не на смешанное содержимое или вложенную разметку клиента.
Шаг 6. Проверьте структуру и преобразуйте типы
Откройте Check Orders, выберите Run Once for All Items и JavaScript. Вставьте весь код:
Показать код (51 строк)
const items = $input.all();
if (items.length !== 1) throw new Error('one_parsed_item_required');
const keys = value => Object.keys(value).sort().join('|');
const data = items[0].json;
if (keys(data) !== 'orders' || !data.orders || Array.isArray(data.orders) ||
typeof data.orders !== 'object') throw new Error('orders_root_required');
const root = data.orders;
if (keys(root) !== '$|order' || !root.$ || keys(root.$) !== 'batch_id' ||
typeof root.$.batch_id !== 'string' || !root.$.batch_id.trim()) {
throw new Error('batch_structure_invalid');
}
if (!Array.isArray(root.order) || !root.order.length) throw new Error('orders_array_required');
const ids = new Set();
let total = 0;
const text = (row, name) => {
const value = row[name];
if (!Array.isArray(value) || value.length !== 1 || typeof value[0] !== 'string') {
throw new Error(name + '_single_text_required');
}
return value[0];
};
const orders = root.order.map(row => {
if (!row || typeof row !== 'object' || Array.isArray(row) ||
keys(row) !== '$|amount_minor|client|paid' || !row.$ || keys(row.$) !== 'id') {
throw new Error('order_structure_invalid');
}
const id = row.$.id;
if (typeof id !== 'string' || !/^\d{3}$/.test(id) || ids.has(id)) {
throw new Error('order_id_invalid_or_duplicate');
}
const client = text(row, 'client');
const rawAmount = text(row, 'amount_minor');
const rawPaid = text(row, 'paid');
if (!client.trim()) throw new Error('client_required');
if (!/^\d+$/.test(rawAmount) || !Number.isSafeInteger(Number(rawAmount))) {
throw new Error('amount_minor_invalid');
}
if (!['true', 'false'].includes(rawPaid)) throw new Error('paid_invalid');
const amount = Number(rawAmount);
total += amount;
if (!Number.isSafeInteger(total)) throw new Error('total_out_of_range');
ids.add(id);
return { order_id: id, client, amount_minor: amount, paid: rawPaid === 'true' };
});
return [{ json: {
batch_id: root.$.batch_id,
orders,
count: orders.length,
total_amount_minor: total,
action_performed: false,
} }];
Код требует точные поля корня и каждого заказа. Для client, amount_minor и paid разрешён ровно один текстовый элемент. Повтор тегов, дополнительные поля и потеря атрибутов останавливают запуск, а не исчезают молча из результата. Это строгий договор именно нашего примера, не универсальная схема XML.
ID остаётся строкой из трёх цифр, включая ведущие нули. Сумма принимается только как непустая строка цифр в безопасном целочисленном диапазоне, затем превращается в Number. paid допускает ровно true или false и преобразуется через сравнение с true. Boolean("false") использовать нельзя: непустая строка дала бы true.
Проверка общей суммы также ограничивает безопасный целочисленный диапазон. Если документ правильный синтаксически, но данные нарушают договор, итоговый JSON не выдаётся за успешный.
Шаг 7. Выполните цепочку целиком
Нажмите Execute Workflow и откройте Output Check Orders. Ожидается один item:
{
"batch_id": "B1",
"orders": [
{
"order_id": "001",
"client": "Анна & Ко",
"amount_minor": 150000,
"paid": false
},
{
"order_id": "002",
"client": "Борис",
"amount_minor": 0,
"paid": true
},
{
"order_id": "003",
"client": "Альфа",
"amount_minor": 49900,
"paid": false
}
],
"count": 3,
"total_amount_minor": 199900,
"action_performed": false
}
Сверьте порядок 001/002/003, имя Анна & Ко, число 0 у 002 и логическое false у 001 и 003. count должен быть 3, total_amount_minor — 199900 копеек, то есть 1999 рублей. batch_id берётся из атрибута корня. action_performed false подтверждает отсутствие внешней записи, а не успешную оплату заказа.
| Поле | После XML-узла | После Check Orders |
|---|---|---|
| id первого заказа | Строка 001 в $ | Строка 001 в order_id |
| amount_minor у 002 | Строка 0 в массиве | Число 0 |
| paid у 001 | Строка false в массиве | Логическое false |
Шаг 8. Оставьте один заказ
В Sample XML временно удалите элементы order с id 002 и 003, оставив корень orders и один заказ 001. Выполните весь workflow. В XML to JSON значение orders.order должно остаться массивом длины 1. В Check Orders ожидаются count 1 и total_amount_minor 150000.
Это проверка настройки Explicit Array. При её выключении структура простых дочерних полей изменится, и наш Check Orders остановится с client_single_text_required. Не исправляйте код под случайный результат одного ответа, если следующий ответ снова может содержать несколько заказов. Верните три исходных элемента.
Шаг 9. Проверьте ошибочные значения
В исходном XML замените сумму первого заказа 150000 на abc. После полного запуска XML-узел сможет разобрать текст, но Check Orders должен остановиться с amount_minor_invalid. Пустое значение, -1 и 1.5 тоже не соответствуют договору целых неотрицательных копеек. Верните 150000.
Теперь замените paid false на yes: ожидается paid_invalid. Затем временно повторите id 001 у второго заказа: ожидается order_id_invalid_or_duplicate. Верните 002. Если деньги в вашем источнике представлены рублями с запятой, это другой договор: не удаляйте разделитель и не округляйте значения автоматически.
Шаг 10. Проверьте сломанный XML и спецсимволы
В Sample XML временно замените закрывающий корневой тег orders на other. Полный запуск должен завершиться ошибкой в XML to JSON, до Check Orders. Точное сообщение парсера может отличаться между версиями; важны узел и отсутствие успешного результата. Верните правильное закрытие.
Затем замените & в имени на неэкранированный амперсанд. Парсер должен отклонить документ. Верните &. На отдельном опыте текст клиента можно представить через CDATA: после разбора значение должно остаться Анна & Ко. Мы проверили оба варианта, но не проверяли произвольную смешанную XML-разметку.
Шаг 11. Убедитесь, что потеря атрибутов заметна
На копии учебного workflow включите Ignore Attributes. В parsed JSON исчезнут batch_id и id, а Check Orders остановится с batch_structure_invalid. Верните Off. Теперь включите Merge Attributes: пути к атрибутам изменятся, и проверка также должна остановиться. Верните Off.
При выключенном Explicit Root пропадёт ожидаемый корень orders, и Check Orders даст orders_root_required. Эти ошибки означают несовпадение структуры с договором, а не необходимость удалить все проверки. После каждого опыта возвращайте исходные параметры.
Шаг 12. Сохраните рабочую схему
Верните три заказа, корректные суммы и paid, уникальные ID и настройки из шага 4. Сохраните и выполните полный workflow. Проверьте один выходной item, count 3, сумму 199900 и отсутствие action_performed true. Схема готова к учебному использованию.
Чтобы потом подать ответ API, получите именно текст и поместите его в согласованное поле xml до Check XML Text. Настоящий запрос, авторизация, пагинация и формат конкретного сервиса требуют отдельного теста: в этой статье внешнего API нет. Бинарный файл тоже сначала нужно преобразовать в текст; строка encoding="UTF-8" сама не перекодирует байты загруженного файла.
Если результат отличается
| Симптом | Причина | Что проверить |
|---|---|---|
| Вход не найден | Не то имя текстового поля | Property Name xml |
| client_single_text_required | Другой тип или повтор тега | Explicit Array и JSON после парсера |
| batch_structure_invalid | Нет атрибутов или они объединены | Ignore Attributes и Merge Attributes |
| orders_root_required | Другой корень или Explicit Root off | Корень orders и Explicit Root on |
| amount_minor_invalid или paid_invalid | Текст не соответствует договору | Значения до преобразования типов |
Где заканчивается эта схема
Это разбор одного небольшого документа известной структуры. Здесь не проверялись SOAP, XML namespaces, XSD-валидация, потоковая обработка, большие файлы и документы произвольной схемы. Синтаксически правильный XML не означает, что заказ настоящий, сумма верна бизнесу или документ разрешено автоматически проводить.
XML-узел выдаёт разобранный документ, а не гарантированно переносит соседние поля входного item. Не рассчитывайте, что внешний request_id останется рядом сам собой: для реального потока нужно отдельно продумать связь документа с запросом. В учебном примере batch_id находится внутри самого XML.
Разделить готовый массив на отдельные items через Split Out
Остановить workflow при нарушении договора данных
Что проверено редакцией
15 октября 2026 года workflow проверен на n8n 2.42.4 в отдельном Docker-экземпляре штатными ручными запусками API. Все 30 проверок прошли: три заказа, один, обратный порядок, CDATA, Trim, пустой/null/пробельный/слишком большой вход, маркер DOCTYPE, сломанное закрытие, неэкранированный амперсанд, другой корень, отсутствие заказов, отсутствующий/повторный/неверный ID, текстовая/отрицательная/пустая/дробная/небезопасная сумма, переполнение итога, неверный paid, повтор client, лишнее поле, Explicit Array off, Explicit Root off, Merge Attributes on и Ignore Attributes on. Сверены промежуточный JSON, строковые атрибуты и суммы до преобразования, точный итог, порядок и отсутствие успешного результата при ошибках. Использован штатный XML версии 1 с xml2js, настройки сверены с официальной документацией и кодом узла. Редакторские клики, Cloud, внешние API, бинарные файлы, SOAP, namespaces, XSD, большие объёмы и полный аудит безопасности XML не тестировались. Учебный контейнер остановлен, чужие workflows не менялись. Обложка — редакционная AI-иллюстрация.
Частые вопросы
Почему после XML получился один item, хотя заказов три?
Один XML-документ превратился в один разобранный JSON-документ. Заказы находятся внутри orders.order, а после нашей проверки — внутри orders. Для отдельных items используйте Split Out.
Зачем включать Explicit Array?
Чтобы структура дочерних элементов не менялась в зависимости от их количества. В примере даже один client хранится массивом из одной строки, и Check Orders ожидает именно это.
Почему ID 001 нельзя превратить в Number?
Это идентификатор, не число для расчёта. Преобразование потеряет ведущие нули. В нашем договоре order_id остаётся строкой из трёх цифр.
Почему paid после XML ещё строка?
В выбранной конфигурации текст тега разбирается как строка. Check Orders явно допускает true/false и преобразует их сравнением, не через Boolean непустого текста.
Можно импортировать настоящий XML-файл без изменений?
Сначала нужно получить текст правильной кодировки и проверить его структуру. Пример рассчитан только на небольшой документ orders с указанными полями. Произвольные файлы, namespaces и большие выгрузки здесь не проверялись.