Как разобрать недопустимый (плохой / не правильно сформированный) XML?
В настоящее время я работаю над функцией, которая включает в себя синтаксический анализ XML, который мы получаем из другого продукта. Я решил провести несколько тестов с некоторыми реальными данными клиента, и похоже, что другой продукт допускает ввод данных от пользователей, который следует считать недопустимым. В любом случае, мне все еще нужно попытаться найти способ его анализа. Мы используем javax.xml.parsers.DocumentBuilder
и я получаю ошибку при вводе, которая выглядит следующим образом.
<xml>
...
<description>Example:Description:<THIS-IS-PART-OF-DESCRIPTION></description>
...
</xml>
Как вы можете видеть, в описании есть то, что кажется недопустимым тегом внутри (<THIS-IS-PART-OF-DESCRIPTION>
). Известно, что этот тег описания является конечным тегом и внутри него не должно быть вложенных тегов. Несмотря на это, это все еще проблема и приводит к исключению при DocumentBuilder.parse(...)
Я знаю, что это недопустимый XML, но он предсказуемо недействителен. Есть идеи о способе анализа такого ввода?
Переведено автоматически
Ответ 1
Этот "XML" хуже, чем недействительный – он неправильно сформирован; см. "Хорошо сформированный" против "допустимого XML".
Неформальная оценка предсказуемости нарушений не помогает. Эти текстовые данные не являются XML. Никакие соответствующие инструменты или библиотеки XML не могут помочь вам обработать их.
Варианты, наиболее желательные в первую очередь:
Попросите провайдера исправить проблему со своей стороны. Требуйте правильно сформированный XML. (Технически фраза правильно сформированный XML избыточна, но может быть полезна для выделения.)
Используйте толерантный анализатор разметки для устранения проблемы перед синтаксическим анализом в формате XML:
xmlstarlet Standalone: обладает надежными возможностями восстановления credit: Романперехрест
xmlstarlet fo -o -R -H -D bad.xml 2>/dev/null
HTML Tidy Standalone и C / C ++: работает и с XML. Taggle - это порт TagSoup на C ++.
Прекрасный суп Python: основан на Python. Смотрите Примечания в разделе "Различия между анализаторами". Смотрите также ответы на этот вопрос, чтобы узнать больше предложений по работе с неправильно сформированной разметкой в Python, включая, в частности,
recover=True
lxml. Смотрите также Этот ответ о том, как использоватьcodecs.EncodedFile()
для очистки от недопустимых символов.TagSoup Java: и JSoup ориентированы на HTML.
FilterInputStream
может использоваться для предварительной обработки очистки..NET:
- Настройки XmlReaderSettings.Контрольные символы можно отключить, чтобы избежать проблем с недопустимыми символами XML.
- @jdweng отмечает, что для этого
XmlReaderSettings.ConformanceLevel
можно установить значениеConformanceLevel.Fragment
, чтобыXmlReader
можно было читать правильно сформированные синтаксические объекты XML, в которых отсутствует корневой элемент. - @jdweng также сообщает, что это
XmlReader.ReadToFollowing()
иногда можно использовать для решения синтаксических проблем XML, но обратите внимание на предупреждение о нарушении правил в # 3 ниже. Microsoft.Language.Xml.XMLParser
говорят, что он “устойчив к ошибкам”.
Перейдите: Установите
Decoder.Strict
значениеfalse
, как показано в этом примере от @chuckx.PHP: Смотрите DOMDocument::$recover и libxml_use_internal_errors(true). Смотрите хороший пример здесь.
Ruby: Nokogiri поддерживает “Мягкую корректность”.
R: Смотрите htmlTreeParse() для анализа отказоустойчивой разметки в R.
Perl: Смотрите XML:: Liberal, "суперлиберальный анализатор XML, который анализирует неработающий XML".
Обработайте данные в виде текста вручную с помощью текстового редактора или программно, используя символьные / строковые функции. Выполнение этого программно может варьироваться от сложного до невозможного, поскольку то, что кажется предсказуемым, часто таковым не является - нарушение правил редко ограничено правилами.
Для ошибок с недопустимыми символами используйте регулярное выражение для удаления / замены недопустимых символов:
- PHP:
preg_replace('/[^\x{0009}\x{000a}\x{000d}\x{0020}-\x{D7FF}\x{E000}-\x{FFFD}]+/u', ' ', $s);
- Ruby:
string.tr("^\u{0009}\u{000a}\u{000d}\u{0020}-\u{D7FF}\u{E000}-\u{FFFD}", ' ')
- JavaScript:
inputStr.replace(/[^\x09\x0A\x0D\x20-\xFF\x85\xA0-\uD7FF\uE000-\uFDCF\uFDE0-\uFFFD]/gm, '')
- PHP:
Для амперсандов используйте регулярное выражение для замены совпадений на
&
: credit: blhsin, demo&(?!(?:#\d+|#x[0-9a-f]+|\w+);)
Обратите внимание, что приведенные выше регулярные выражения не учитывают комментарии или CDATA
разделы.
Ответ 2
Стандартный анализатор XML НИКОГДА не примет недопустимый XML по своей конструкции.
Ваш единственный вариант - предварительно обработать входные данные, чтобы удалить "предсказуемо недопустимое" содержимое, или обернуть его в CDATA, перед его разбором.
Ответ 3
Принятый ответ является хорошим советом и содержит очень полезные ссылки.
Я хотел бы добавить, что этот и многие другие случаи неправильно сформированного и / или DTD-недопустимого XML могут быть исправлены с помощью SGML, стандартизированного ISO надмножества HTML и XML. В вашем случае работает объявление фиктивного THIS-IS-PART-OF-DESCRIPTION
элемента как SGML empty element, а затем использование, например. osx
программа (часть пакета OpenSP / OpenJade SGML) для преобразования его в XML. Например, если вы предоставите следующее в osx
<!DOCTYPE xml [
<!ELEMENT xml - - ANY>
<!ELEMENT description - - ANY>
<!ELEMENT THIS-IS-PART-OF-DESCRIPTION - - EMPTY>
]>
<xml>
<description>blah blah
<THIS-IS-PART-OF-DESCRIPTION>
</description>
</xml>
он выведет правильно сформированный XML для дальнейшей обработки с помощью инструментов XML по вашему выбору.
Обратите внимание, однако, что в вашем примере фрагмента есть другая проблема, заключающаяся в том, что имена элементов, начинающиеся с букв xml
или XML
или Xml
и т.д., зарезервированы в XML и не будут приняты соответствующими анализаторами XML.
Ответ 4
IMO, эти случаи следует решать с помощью JSoup.
Ниже приведен не совсем ответ для данного конкретного случая, но я нашел это в Интернете (благодаря inuyasha82 на Coderwall). Этот фрагмент кода вдохновил меня на решение другой подобной проблемы при работе с неверно сформированными XML-файлами, поэтому я делюсь им здесь.
Пожалуйста, не редактируйте то, что ниже, так как оно есть на оригинальном веб-сайте.
Формат XML требует, чтобы был допустимым уникальный корневой элемент, объявленный в документе.
Так, например, допустимый xml - это:
<root>
<element>...</element>
<element>...</element>
</root>
Но если у вас есть документ, подобный:
<element>...</element>
<element>...</element>
<element>...</element>
<element>...</element>
Это будет считаться неправильно сформированным XML, поэтому многие анализаторы xml просто выдают исключение с жалобой на отсутствие корневого элемента. И т.д.
В этом примере есть решение о том, как решить эту проблему и успешно разобрать искаженный xml, приведенный выше.
В основном, что мы сделаем, так это добавим программно корневой элемент.
Итак, прежде всего вам нужно открыть ресурс, содержащий ваш "искаженный" xml (т. е. файл):
File file = new File(pathtofile);
Затем откройте FileInputStream:
FileInputStream fis = new FileInputStream(file);
Если мы попытаемся разобрать этот поток с помощью любой библиотеки XML, в этот момент мы вызовем исключение с искаженным документом.
Теперь мы создаем список объектов InputStream с тремя элементами:
- Элемент ByteIputStream, содержащий строку:
<root>
- Наш FileInputStream
- ByteInputStream со строкой:
</root>
Итак, код:
List<InputStream> streams =
Arrays.asList(
new ByteArrayInputStream("<root>".getBytes()),
fis,
new ByteArrayInputStream("</root>".getBytes()));
Теперь, используя SequenceInputStream, мы создаем контейнер для списка, созданного выше:
InputStream cntr =
new SequenceInputStream(Collections.enumeration(str));
Теперь мы можем использовать любую библиотеку синтаксического анализа XML в cntr, и она будет проанализирована без каких-либо проблем. (Проверено с помощью библиотеки Stax);