Конвертер XML в JSON

Преобразуйте XML в JSON или JSON в XML: атрибуты через @, #text, массивы, решённые по всему документу, и строка и столбец, где XML построен некорректно.

Отступ
Ввод
Вывод

Здесь появится вывод

Что делает этот инструмент

XML обычно приходит из систем, которые никто не выбирал: ответ SOAP, лента RSS, файл конфигурации, выгрузка из чего-то более старого, чем JSON. А коду, который должен с ним работать, нужен JSON. Обратное встречается не реже: полезная нагрузка JSON, предназначенная для системы, которая принимает только XML. Эта страница преобразует в обоих направлениях, прямо в вашем браузере, и читает оба формата по одному неизменному набору правил, поэтому оба направления согласны в том, что означает каждый ключ.

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

Правила: @ для атрибута, #text для текста

Набор правил один, и никакая настройка его не меняет. Атрибут становится ключом, записанным как @ и имя атрибута, а текст, которому приходится делить свой элемент с чем-то ещё, помещается под ключ #text. Имя XML не может начинаться ни с @, ни с #, поэтому ключ, записанный таким образом, нельзя спутать с дочерним элементом, и именно это позволяет прочитать тот же JSON обратно в XML без догадок. На примере небольшого каталога:

  • Корневой элемент — единственный ключ верхнего уровня: документ, корневой элемент которого — catalog, становится объектом с единственным ключом "catalog".
  • Элемент, в котором нет ничего, кроме текста, и есть этот текст: <title>Midnight Rain</title> становится "title": "Midnight Rain".
  • Атрибут — это ключ рядом с дочерними элементами: <book id="bk101"> даёт книге "@id": "bk101", а каждый дочерний элемент следует за ним как ключ, в порядке своего первого появления.
  • Элемент с атрибутами и текстом хранит текст под #text: <price currency="USD">44.95</price> становится "price": {"@currency": "USD", "#text": 44.95}.
  • Элемент, повторяющийся под одним родителем, становится массивом: у книги, элемент author которой встречается дважды, есть "author": ["Gambardella, Matthew", "Knorr, Stefan"].
  • Имена сохраняются в точности так, как записаны, вместе с префиксом: <dc:title> становится ключом "dc:title", объявление пространства имён становится ключом атрибута, например "@xmlns:dc", и ничто не разрешается в URI пространства имён.

Сущности и ссылки на символы декодируются, так что &amp; приходит как &, а &#233; — как é; секция CDATA — это просто текст. Пробельные символы, стоящие отдельно между элементами, — это форматирование, и они отбрасываются; любой другой фрагмент текста сохраняется целиком и никогда не обрезается. Но прежде всё равно выполняются две нормализации, которые применяет любой соответствующий стандарту парсер XML: возврат каретки, набранный в тексте, приходит как перевод строки, а табуляция или перевод строки, набранные в значении атрибута, приходят как пробел. Ссылка на символ — это то, чем документ сохраняет любой из них.

Почему структуру решает весь документ

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

Поэтому структура решается по пути: это имена элементов от корня вниз до элемента, записанные как /catalog/book/author, без номеров позиций, так что каждый автор каждой книги стоит на одном пути. Все элементы на пути взвешиваются разом:

  • Массивы. Если элемент повторяется хотя бы под одним родителем, каждый элемент на пути записывается массивом, включая одиночный.
  • Объекты. Если хотя бы один элемент на пути имеет атрибут или дочерний элемент, каждый элемент на нём записывается объектом, включая тот, в котором только текст, и хранит свой текст под #text.
  • Значения. Путь читается как числа, только если преобразуется каждое значение на нём, и как логические значения, только если каждое значение — true или false, по правилу, описанному ниже в разделе о числах и логических значениях.
  • Пустые элементы. Пустой элемент в этих решениях не участвует и получает тот вид, на котором остановился его путь.

Везде, где результат отличается от того, чем элемент был бы сам по себе, страница сообщает об этом один раз для пути, со строкой и столбцом первого элемента, который это изменило. В каталоге, где у одной книги несколько авторов, а у другой только один, этот единственный автор всё равно будет массивом; там, где одна цена несёт атрибут currency, а другая нет, простая цена всё равно будет объектом с текстом под #text. Об обоих случаях сообщается, так что ни тот ни другой не застанет врасплох код, который их читает.

Образец с одной записью и поле «Всегда массивы»

Структура хороша ровно настолько, насколько хорош документ, по которому она решена. Вставьте каталог с одной книгой — и эта книга будет объектом, её единственный автор — строкой, а цена — числом, тогда как тот же каталог со второй книгой рядом сделает книги массивом. Путь, который во всём вставленном встречается по одному разу, остаётся одиночным значением, а путь, все значения которого случайно преобразуются, остаётся числами, пока не придёт документ с почтовым индексом, начинающимся с нуля. JSON надёжен ровно настолько, насколько представителен образец, поэтому вставляйте больше одной записи, когда только можете.

Для массивов есть средство, не зависящее от образца. Введите в «Всегда массивы» имена элементов или пути, которые должны быть массивами, что бы ни показывал документ, разделяя их запятыми или пробелами, — и каждый путь, которому соответствует пункт поля, везде будет записан массивом, даже там, где он встречается один раз:

  • Простое имя, например item, соответствует каждому пути, который заканчивается элементом с именем item, где бы он ни стоял.
  • Путь, например /rss/channel/item, соответствует ровно этому пути и записывается так же, как пути под выводом, поэтому его можно скопировать оттуда.
  • Совпадение точное: регистр важен, а префикс — часть имени, поэтому Item не соответствует ни одному item, а link — ни одному atom:link.
  • О каждом пути, который поле сделало массивом, сообщается, как о любом другом решении. Пункт, который не является ни именем, ни путём, и пункт, не соответствующий ни одному элементу документа, перечисляются под полем, а подходящие пункты продолжают действовать.

Поле относится к направлению «XML в JSON». Оно сохраняет введённое, когда вы переключаете направление туда и обратно, и, как любая настройка на странице, не сохраняется между визитами. Для значений такого поля нет: путь, который должен оставаться текстом, что бы ни содержал образец, — это как раз то, для чего выключают «Преобразовывать числа и логические значения», ценой того, что все остальные значения тоже станут текстом.

Числа, логические значения и почему 02134 остаётся текстом

В XML всё — текст, и превращать похожий на числа текст в числа JSON обычно именно то, что нужно. Но именно так почтовый индекс 02134 становится 2134, а длинный номер заказа теряет последние цифры. Правило здесь то же, что использует конвертер CSV на этом сайте, и ему не нужен список особых случаев: текст становится числом, только если это число, записанное обратно, в точности воспроизводит текст. 42 воспроизводит — значит, преобразуется. 02134 нет, как и 1.50, +5, 1e5 или целое число, слишком длинное, чтобы число двойной точности удержало все его цифры. Логические значения — это ровно true и false, в нижнем регистре.

Эта страница добавляет то, что правило применяется ко всему пути сразу. Путь читается как числа, только если преобразуется каждое значение на нём, и как логические значения, только если каждое значение — логическое; одно значение, которое потеряло бы цифру, или числа и логические значения, встретившиеся на одном пути, оставляют текстом каждое значение там. Каждое имя атрибута на пути решается отдельно. О пути, оставленном текстом, хотя некоторые его значения преобразовались бы, сообщается с названием пути, и для атрибута путь заканчивается на атрибуте, как в /catalog/book/@id.

«Преобразовывать числа и логические значения» включено, когда страница открывается, так же как включён соответствующий переключатель на странице CSV, а если его выключить, каждое значение записывается текстом. Значение объявления пространства имён ни при каком положении переключателя не читается как число, потому что стандарт Namespaces in XML определяет это значение как имя пространства имён, то есть текст.

Пустые элементы и null, который задаёт сам документ

  • Пустой элемент получает тот вид, на котором остановился его путь. На текстовом пути это пустая строка. На пути чисел или логических значений это null, и страница об этом сообщает, поскольку сам по себе элемент был бы пустым текстом.
  • Пустой — значит совсем ничего: <zip> </zip> содержит пробел, а пробел — это текст.
  • Путь, на котором все элементы пусты, текстовый, поэтому каждый из них — пустая строка. Ничто там не читается как число, а null был бы информацией, которой документ никогда не давал.
  • На пути, записанном объектами, пустой элемент тоже несёт #text, пустой или null по тому же правилу, везде, где другой элемент на пути содержит текст: рядом с <price currency="USD">5</price> элемент <price/> становится {"#text": null}. Там, где ни один элемент на пути не содержит текста, как у элементов изображения, у которых есть только атрибут src, ни один из них не несёт #text.

Документ может и сам задать null. Элемент, помеченный xsi:nil="true", где этот префикс связан с пространством имён XML Schema instance, равен null на любом пути, при любом положении «Преобразовывать числа и логические значения». Эта пометка — способ документа сказать null, а не данные, поэтому в JSON не появляются ни сам атрибут, ни объявление пространства имён, не связывающее ничего другого. Подходит любой префикс, связанный с этим пространством имён, и i:nil="1" говорит то же самое; xsi:nil="false" — обычные данные, а элемент, помеченный как nil, но всё ещё имеющий содержимое, остаётся как есть, поскольку прочитать его как null значило бы молча отбросить это содержимое.

Чему нет места в JSON: не потеряно, а названо

Часть того, что содержит документ XML, в JSON места не имеет. То, что несёт смысл, не отбрасывается молча, а называется под выводом:

  • Порядок смешанного содержимого. В <p>Hello <b>world</b>, again</p> текст собирается под #text как "Hello , again", а элемент b — под "b": каждый символ и каждый элемент сохраняются, но не порядок, в котором они стояли. Дочерние элементы, чередующиеся по именам, так же теряют свой порядок. Об этом сообщается один раз для пути элемента, которому принадлежит содержимое, и только там, где порядок действительно потерян, поэтому <p><b>Note:</b> the rest</p> ничего не вызывает: его сохраняет порядок ключей.
  • Комментарии и инструкции обработки. Они опускаются, с одним замечанием на каждый вид, где сказано, сколько их было и где стоит первое из них.
  • DOCTYPE. Он тоже опускается, и замечание о нём говорит, что DTD не применялось: значение атрибута по умолчанию, объявленное в DTD, в JSON отсутствует, потому что этот инструмент не читает DTD.
  • Объявление XML в начале документа. Оно опускается без замечания, потому что описывает, как закодирован текст, а не то, что представляют собой данные.

JSON обратно в XML: ближайший XML, и каждое изменение названо

Переключите направление, и те же правила работают в обратную сторону: ключ @ становится атрибутом, #text — текстом своего элемента, массив — отдельным элементом для каждого члена, а любой другой ключ — дочерним элементом, причём дочерние элементы идут в порядке своих ключей. Значение null записывается как пустой элемент, помеченный xsi:nil="true", а пространство имён XML Schema instance объявляется один раз на корневом элементе и только когда записан null, поэтому обратное преобразование снова даёт null, а не пустую строку. Кроме JSON, который не разбирается или вложен слишком глубоко для преобразования, ничего не отвергается. Там, где у вашего JSON структура, которую XML не может передать как есть, записывается ближайший корректно построенный XML, а изменение называется под выводом по JSONPath, который можно вставить в тестер JSONPath на этом сайте:

  • Всё, кроме единственного корневого элемента, — несколько ключей на верхнем уровне, ни одного, массив верхнего уровня или простое значение — записывается внутрь <root>.
  • Ключ, который не является именем, допустимым в XML, записывается под ближайшим именем, под которым не записан ни один другой ключ рядом: first name как first_name, а 1st как _1st.
  • Член массива, которому ни один ключ не даёт имени, — стоит ли этот массив внутри другого массива или на верхнем уровне, — записывается как элемент <item>.
  • Ключ @ или #text, содержащий объект или массив, записывается как дочерний элемент, поскольку ни атрибут, ни текст не могут этого содержать.
  • Ключ @, содержащий null, опускается, как и #text, содержащий null рядом с дочерними элементами, поскольку XML говорит null только о целом элементе.
  • Символ, который XML 1.0 не может содержать вовсе, даже в виде ссылки, заменяется, и страница сообщает где.

Экранирование — часть записи корректно построенного XML, а не опция. Символы < и & экранируются всегда. В значении атрибута экранируется и двойная кавычка, потому что значение записывается между двумя такими кавычками; табуляция, перевод строки или возврат каретки там записываются как ссылка на символ, потому что иначе парсер превратил бы их в пробел. В тексте возврат каретки записывается как ссылка, потому что иначе парсер превратил бы его в перевод строки, а ]]> никогда не записывается как есть. Именно это позволяет значению прочитаться обратно тем значением, которое вы записали.

Что даст обратное преобразование — сказано до того, как вы преобразуете

Бывает, что XML корректно построен и всё же читается обратно не как тот JSON, из которого был записан, поэтому страница читает только что записанный ею XML по тем же правилам, что использует другое направление, и сообщает, где ответ отличается. Массив из одного члена читается обратно как этот единственный член, если только те же элементы не повторяются под одним родителем где-то ещё в документе. Пустой массив не записывает ни одного элемента, а пустой #text — никакого текста, поэтому и то и другое читается обратно как ничто. Объект, не содержащий ничего, что записывается как атрибут или дочерний элемент, читается обратно как простое значение, а там, где те же элементы в другом месте заставляют документ решить массив или объект, которого не было в вашем JSON, об этом тоже говорится. Каждый из этих случаев записывается так, как он есть в вашем JSON, и о нём сообщается.

Кнопка «Использовать как ввод» переносит вывод в поле ввода и переключает направление, так что то, что возвращается, и то, о чём при этом сообщается, — на расстоянии одного щелчка. Там, где ни о чём не сообщается, возвращается ваш JSON, за вычетом того, чего XML не хранит. Текст XML не записывает, было ли значение числом, логическим значением или строкой: при включённом «Преобразовывать числа и логические значения» строка, в точности записывающая число, может вернуться этим числом, а при выключенном каждое значение возвращается текстом. А атрибут или пустой либо равный null #text могут вернуться на другое место среди соседних ключей, потому что XML это положение не хранит.

Круг, начатый со стороны XML, обещает меньше, и это намеренно. Вставленный вами текст не возвращается: объявление, форматирование, комментарии и всё прочее, о чём было сообщено на пути туда, пропадает. Неизменным остаётся XML, который пишет страница: преобразуйте его в JSON и обратно с теми же настройками, и вернётся тот же XML.

Когда XML или JSON не читается

Документ, который парсер XML не может прочитать, отвергается с предложением, говорящим, что не так, и со строкой и столбцом везде, где есть место, которое можно указать: у незакрытого элемента, у значения атрибута без кавычек и у символа &, который не начинает ссылку, — у каждого своё предложение. Закрывающий тег, который не соответствует ещё открытому элементу, говорит об этом, а затем показывает закрывающий тег, нужный этому элементу, и в нём назван элемент, оставшийся открытым. JSON, который не разбирается, отвергается так же, со строкой и столбцом, где остановился его разбор, везде, где это можно измерить.

Под предложением, в котором указано место, страница показывает вашу собственную строку и отмечает в ней символ, на котором произошёл сбой, — это важнее всего для сжатого XML или JSON, где весь документ — одна строка. Длинная строка сокращается до участка вокруг отметки, с многоточием там, где она обрезана. Символ сбоя, который ничего не рисует, например конец текста, табуляция или метка порядка байтов, отмечается на видимом заменителе. Столбец считается так, как JavaScript считает строку, — в единицах кода UTF-16, поэтому эмодзи или редкий иероглиф перед отметкой сдвигает его на два, а не на один.

Документ, вложенный глубже, чем допускает сайт, тоже отвергается, в любом направлении, и в его предложении назван предел.

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

Почему одиночный элемент записан массивом?
Потому что элемент на том же пути повторяется под одним родителем где-то ещё в документе, а путь решается один раз для всего документа. Код, который читает значение как массив, тогда работает для каждой записи, включая записи с одним элементом. Страница называет путь со строкой и столбцом первого элемента, который это изменило. Чтобы получить массив там, где в вашем образце только один элемент, впишите имя элемента в «Всегда массивы».
Почему число в моём JSON в кавычках?
Потому что какое-то значение на том же пути не сохранилось бы как число — из-за ведущего нуля, конечного нуля после десятичной точки, знака плюс или экспоненты — или потому что на этом пути встречаются числа и логические значения. Такой путь везде текстовый, и страница его называет. Его значения преобразуются, как только каждое из них будет записано так, как число записывается обратно: 2134, а не 02134.
Что происходит с комментариями, инструкциями обработки и DOCTYPE?
Они не попадают в JSON, и о каждом виде сообщается, сколько их было и где стоит первое из них. Замечание о DOCTYPE также говорит, что DTD не применялось, поэтому любого объявленного в нём значения атрибута по умолчанию нет.
Проверяет ли он мой XML по DTD или XSD?
Нет. Он читает корректно построенный XML и сообщает, где не смог прочитать документ, но не проверяет его по DTD или XSD и не применяет ничего из того, что объявляет DTD. Именно поэтому DOCTYPE не игнорируется молча: о нём сообщается, что он не применён.
Как null записывается в XML и возвращается ли он?
Как пустой элемент, помеченный xsi:nil="true", с пространством имён XML Schema instance, объявленным один раз на корневом элементе. Обратное преобразование этого XML снова даёт null, тогда как простой пустой элемент вернулся бы пустой строкой на любом пути, читаемом как текст. Если ваш JSON уже использует префикс xsi для собственного имени, страница объявляет вместо него свободный префикс.
Что происходит с ключом, в котором есть пробел?
Он записывается под ближайшим именем, которое допускает XML, так что first name становится first_name, а страница называет ключ по его JSONPath вместе с именем, которым он стал. Вставьте этот JSONPath в тестер JSONPath на этом сайте, чтобы выбрать каждое значение, к которому применилось изменение.
Почему обратное преобразование не возвращает мой исходный XML?
Потому что в JSON нет места для части того, что содержит файл XML: объявления, форматирования между элементами, комментариев, инструкций обработки, DOCTYPE и порядка смешанного содержимого. О том, что несло смысл, было сообщено на пути туда. Неизменным остаётся XML, который пишет страница: переведите его в JSON и обратно с теми же настройками, и вернётся тот же XML.
Отправляется ли что-нибудь из вставленного на сервер?
Нет. Оба направления выполняются целиком в вашем браузере, поэтому документ, который вы вставляете, будь то ответ API, лента или файл конфигурации с учётными данными, никогда не покидает ваше устройство.

Похожие инструменты