DevToys Pro

бесплатные веб-инструменты для разработчиков

Блог
Оцените нас:
Попробуйте расширение для браузера:
← Назад к блогу

Правила отображения XML→JSON: Атрибуты, #text и обработка массивов

16 мин чтения

Вы конвертируете XML в JSON, и результат не соответствует вашим ожиданиям: атрибуты превращаются в @attr, текстовый контент становится #text, а иногда появляются массивы там, где вы ожидали объекты. Понимание правил отображения XML↔JSON помогает предсказать результат конвертации и правильно структурировать данные для обратимых преобразований.

Фундаментальная проблема: XML ≠ JSON

XML и JSON имеют разные модели данных:

  • XML: Элементы могут иметь атрибуты, текстовый контент и дочерние элементы одновременно
  • JSON: Объекты имеют пары ключ-значение; нет различия между атрибутами и дочерними элементами

Не существует единственно "правильного" способа конвертации между ними, но появились общие соглашения.

Правило 1: Элементы становятся объектами

Простые XML элементы преобразуются в JSON объекты:

<!-- XML -->
<person>
  <name>Alice</name>
  <age>30</age>
</person>
{
  "person": {
    "name": "Alice",
    "age": "30"
  }
}

Правило 2: Атрибуты получают специальный префикс

XML атрибуты обычно становятся JSON ключами с префиксом @:

<!-- XML -->
<person id="123" active="true">
  <name>Alice</name>
</person>
{
  "person": {
    "@id": "123",
    "@active": "true",
    "name": "Alice"
  }
}

Префикс @ отличает атрибуты от дочерних элементов. Без него вы не могли бы отличить <person id="123"> от <person><id>123</id></person>.

Правило 3: Текстовый контент становится #text

Когда элемент имеет и атрибуты, и текстовый контент, текст становится специальным ключом #text:

<!-- XML -->
<price currency="USD">49.99</price>
{
  "price": {
    "@currency": "USD",
    "#text": "49.99"
  }
}

Без ключа #text не было бы способа представить и атрибут currency, и текстовое значение 49.99.

Простой текст (без атрибутов) = прямое значение

Если элемент содержит только текстовый контент (без атрибутов, без дочерних элементов), он становится простой строкой:

<!-- XML -->
<name>Alice</name>
{
  "name": "Alice"
}

Обёртка #text не нужна, потому что нет неоднозначности.

Правило 4: Повторяющиеся элементы становятся массивами

Несколько элементов с одинаковым именем преобразуются в JSON массив:

<!-- XML -->
<items>
  <item>Widget A</item>
  <item>Widget B</item>
  <item>Widget C</item>
</items>
{
  "items": {
    "item": [
      "Widget A",
      "Widget B",
      "Widget C"
    ]
  }
}

Единичный элемент ≠ массив

Это распространённая ловушка. Единичный элемент не становится массивом:

<!-- XML -->
<items>
  <item>Widget A</item>
</items>
{
  "items": {
    "item": "Widget A"    // Строка, не массив!
  }
}

Это означает, что ваш код должен проверять, является ли item строкой или массивом. Некоторые конвертеры имеют опцию "всегда использовать массивы", чтобы избежать этой несогласованности.

Правило 5: Пустые элементы становятся пустыми объектами или null

Пустые XML элементы обычно становятся пустыми объектами или null:

<!-- XML -->
<data>
  <empty></empty>
  <selfClosing/>
</data>
{
  "data": {
    "empty": {},
    "selfClosing": {}
  }
}

Или, в зависимости от конвертера:

{
  "data": {
    "empty": null,
    "selfClosing": null
  }
}

Реальный пример: Ответ API

Вот реалистичный ответ SOAP API и его JSON конвертация:

XML ответ API

<?xml version="1.0" encoding="UTF-8"?>
<response status="success" timestamp="2026-01-17T10:30:00Z">
  <user id="12345" verified="true">
    <name>Alice Johnson</name>
    <email>alice@example.com</email>
    <roles>
      <role priority="high">admin</role>
      <role priority="normal">user</role>
    </roles>
    <metadata>
      <created>2020-01-15</created>
      <lastLogin>2026-01-16T08:00:00Z</lastLogin>
    </metadata>
  </user>
</response>

JSON конвертация

{
  "response": {
    "@status": "success",
    "@timestamp": "2026-01-17T10:30:00Z",
    "user": {
      "@id": "12345",
      "@verified": "true",
      "name": "Alice Johnson",
      "email": "alice@example.com",
      "roles": {
        "role": [
          {
            "@priority": "high",
            "#text": "admin"
          },
          {
            "@priority": "normal",
            "#text": "user"
          }
        ]
      },
      "metadata": {
        "created": "2020-01-15",
        "lastLogin": "2026-01-16T08:00:00Z"
      }
    }
  }
}

Ключевые наблюдения:

  • Корневые атрибуты status и timestamp становятся @status и @timestamp
  • Атрибуты пользователя id и verified становятся @id и @verified
  • Простые элементы как name и email становятся обычными строками
  • Повторяющиеся элементы role становятся массивом
  • Каждый role имеет атрибут и текстовый контент, поэтому он становится объектом с @priority и #text

Обратная конвертация JSON в XML

Обратная конвертация (JSON → XML) следует тем же правилам, но требует внимательной обработки:

{
  "order": {
    "@id": "ORD-001",
    "customer": "Alice",
    "items": {
      "item": [
        "Widget A",
        "Widget B"
      ]
    },
    "total": {
      "@currency": "USD",
      "#text": "99.99"
    }
  }
}

Преобразуется в:

<order id="ORD-001">
  <customer>Alice</customer>
  <items>
    <item>Widget A</item>
    <item>Widget B</item>
  </items>
  <total currency="USD">99.99</total>
</order>

Применённые правила:

  • Ключи, начинающиеся с @, становятся атрибутами
  • #text становится текстовым контентом
  • Массивы становятся повторяющимися элементами
  • Простые строковые значения становятся текстовым контентом

Пространства имён: Усложнение

XML пространства имён добавляют сложность в JSON конвертацию:

<!-- XML с пространством имён -->
<order xmlns="http://example.com/orders" xmlns:cust="http://example.com/customer">
  <cust:customer id="123">
    <cust:name>Alice</cust:name>
  </cust:customer>
  <item>Widget</item>
</order>

Разные конвертеры обрабатывают это по-разному:

Вариант 1: Сохранение префиксов

{
  "order": {
    "@xmlns": "http://example.com/orders",
    "@xmlns:cust": "http://example.com/customer",
    "cust:customer": {
      "@id": "123",
      "cust:name": "Alice"
    },
    "item": "Widget"
  }
}

Вариант 2: Расширение пространств имён

{
  "{http://example.com/orders}order": {
    "{http://example.com/customer}customer": {
      "@id": "123",
      "{http://example.com/customer}name": "Alice"
    },
    "{http://example.com/orders}item": "Widget"
  }
}

Используйте XML ↔ JSON конвертер, чтобы протестировать, как ваш XML с пространствами имён конвертируется.

Смешанный контент: Граничный случай

Смешанный контент (текст и элементы смешаны вместе) является валидным XML, но неудобен в JSON:

<!-- XML со смешанным контентом -->
<description>
  Цена составляет <price currency="USD">49.99</price> только для новых клиентов.
</description>

Это не может быть чисто представлено в JSON, потому что JSON объекты не сохраняют порядок и не позволяют текст между ключами. Конвертеры обычно обрабатывают это, создавая массив:

{
  "description": [
    "Цена составляет ",
    {
      "price": {
        "@currency": "USD",
        "#text": "49.99"
      }
    },
    " только для новых клиентов."
  ]
}

Смешанный контент редко встречается в XML, ориентированном на данные (как API), но часто в XML, ориентированном на документы (как HTML или DocBook).

Секции CDATA

Секции CDATA сохраняют специальные символы без экранирования:

<!-- XML -->
<code><![CDATA[
  if (x < 10 && y > 5) {
    console.log("Hello");
  }
]]></code>

В JSON, CDATA становится обычным текстом:

{
  "code": "\n  if (x < 10 && y > 5) {\n    console.log(\"Hello\");\n  }\n"
}

Обёртка CDATA теряется, но контент сохраняется с правильным экранированием.

Комментарии теряются

XML комментарии не преобразуются в JSON (в JSON нет синтаксиса комментариев):

<!-- XML -->
<config>
  <!-- Это важно -->
  <setting>value</setting>
</config>
{
  "config": {
    "setting": "value"
  }
}

Комментарии молча отбрасываются во время конвертации.

Реальный сценарий отладки

Вы получаете XML из SOAP API и нужно работать с ним в JavaScript. Вот процесс:

1. Конвертировать XML в JSON

<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
  <soap:Body>
    <GetUserResponse xmlns="http://api.example.com">
      <user id="12345">
        <name>Alice</name>
        <roles>
          <role>admin</role>
          <role>user</role>
        </roles>
      </user>
    </GetUserResponse>
  </soap:Body>
</soap:Envelope>

Вставьте в XML ↔ JSON конвертер

2. Понять структуру JSON

{
  "soap:Envelope": {
    "@xmlns:soap": "http://schemas.xmlsoap.org/soap/envelope/",
    "soap:Body": {
      "GetUserResponse": {
        "@xmlns": "http://api.example.com",
        "user": {
          "@id": "12345",
          "name": "Alice",
          "roles": {
            "role": ["admin", "user"]
          }
        }
      }
    }
  }
}

3. Доступ к данным в JavaScript

const envelope = JSON.parse(jsonString);
const user = envelope["soap:Envelope"]["soap:Body"].GetUserResponse.user;

console.log(user["@id"]);      // "12345"
console.log(user.name);         // "Alice"
console.log(user.roles.role);   // ["admin", "user"]

// Проверка, является ли role массивом (обработка одного vs нескольких ролей)
const roles = Array.isArray(user.roles.role) 
  ? user.roles.role 
  : [user.roles.role];

roles.forEach(role => console.log(role));

Распространённые ловушки и решения

Ловушка 1: Один vs несколько элементов

Проблема: Код ломается, когда в XML один <item> вместо нескольких

Решение: Всегда проверяйте, является ли значение массивом

// Защитный парсинг
const items = data.items.item;
const itemArray = Array.isArray(items) ? items : [items];

itemArray.forEach(item => {
  console.log(item);
});

Ловушка 2: Доступ к атрибутам

Проблема: Забывание префикса @ для атрибутов

Решение: Помните, что атрибуты имеют префикс @

// Неправильно
console.log(user.id);        // undefined

// Правильно
console.log(user["@id"]);    // "12345"
// Или используйте скобочную нотацию
console.log(user["@id"]);

Ловушка 3: Текст + атрибуты

Проблема: Ожидание простого значения, когда элемент имеет атрибуты

<price currency="USD">49.99</price>
// Неправильно - price это объект, а не строка
console.log(data.price);  // { "@currency": "USD", "#text": "49.99" }

// Правильно
console.log(data.price["#text"]);      // "49.99"
console.log(data.price["@currency"]); // "USD"

Инструменты для тестирования конвертации

Используйте XML ↔ JSON конвертер, чтобы:

  • Тестировать конвертацию XML → JSON с реальными данными
  • Увидеть, как обрабатываются атрибуты, массивы и пространства имён
  • Проверить обратимую конвертацию (XML → JSON → XML)
  • Отлаживать структуры ответов API

Краткая справка: Правила отображения

Паттерн XMLРезультат JSONПримечания
<name>Alice</name>{"name": "Alice"}Простой элемент → строка
<user id="123">{"@id": "123"}Атрибут → @ключ
<price currency="USD">49.99</price>{"@currency": "USD", "#text": "49.99"}Атрибут + текст
<item>A</item><item>B</item>{"item": ["A", "B"]}Повторяющиеся → массив
<item>A</item> (один){"item": "A"}Единичный → не массив
<empty/>{"empty": {}} или nullПустой элемент
<!-- комментарий -->ПропущенНет эквивалента в JSON
<![CDATA[...]]>Обычный текст (экранирован)Обёртка CDATA потеряна
<ns:element>{"ns:element": ...}Префикс пространства имён сохранён

Лучшие практики

  • Тестируйте конвертации - Используйте инструменты конвертации с реальными данными перед написанием кода
  • Обрабатывайте массивы защитно - Всегда проверяйте, является ли значение массивом или одним элементом
  • Помните префиксы - @ для атрибутов, #text для смешанного контента
  • Документируйте структуру - Отмечайте, какие поля являются массивами, какие атрибутами
  • Избегайте смешанного контента - Проектируйте XML, ориентированный на данные, без текста между элементами
  • Рассмотрите схемы - XSD схемы помогают предсказать массив vs единичный элемент

Резюме

Конвертация XML в JSON следует предсказуемым правилам:

  • Элементы → объекты, атрибуты → @ключ
  • Текст с атрибутами → ключ #text
  • Повторяющиеся элементы → массивы (но единичный элемент остаётся единичным)
  • Пустые элементы → пустые объекты или null
  • Пространства имён сохраняются как префиксы или расширенные URI
  • Комментарии и секции CDATA теряются/сплющиваются

Понимание этих правил помогает предсказать результат конвертации и писать надёжный код парсинга. Используйте XML ↔ JSON конвертер, чтобы тестировать конвертации с вашими реальными данными.


Нужно конвертировать между XML и JSON? Используйте XML ↔ JSON конвертер для тестирования конвертаций и понимания, как будут структурированы ваши данные. Также посмотрите JSON форматтер для очистки преобразованного JSON.