Практическая работа с XML: Опыт безопасного написания документов и приложений. Часть 1 (исходники)

Бенуа Маршаль, консультант, Pineapplesoft

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

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

Когда XML был представлен публике впервые, организации и разработчики отнеслись к этому новому языку разметки с вежливым подозрением. Но по мере того как они использовали этот язык для решения все большего числа проблем, подозрительность сменилась энтузиазмом. Сегодня уже очень многие разработчики и организации используют XML в своих проектах.

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

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

Для этой серии из четырех статей, я просмотрел свои записи и отыскал наиболее часто встречающиеся "ловушки" в XML . Я надеюсь, что задокументировав их и представив альтернативы, я помогу вам преодолеть основные препятствия технологии.

Я начну с фундаментального -,собственно, с XML.Приверженность общепринятому синтаксису - это первый шаг на пути создания надежных приложений. Ниже рассматриваются следующие общие вопросы:

  • Использование парсеров и управляющие последовательности для зарезервированных символов;
  • Кодировка;
  • Пространства имен.

Следующии статьи рассмотрят, как использовать XML-документы наверняка, как проверять и тестировать XML -документы и как сопрячь XML со многими другими файловыми форматами, такими как изображения, фильмы, текст и т.д.

Простой синтаксис

В первом разделе обсуждаются некоторые общие вопросы синтаксиса XML.

Синтаксис XML достаточно простой: просто необходимо соблюдать баланс между открывающими и закрывающими тэгами. Тем не менее автор очень хотел бы получать знаменитый пятицентовик всякий раз получая электронное письмо, отправитель которого сетует на то, что ему не удалось обработать прилагаемый к письму документ XML ни одним из известных ему парсеров. Неизменно при открытии присланного документа XML автор обнаруживает очевидную синтаксическую ошибку - пустой тэг без закрывающей косой черты (например: <empty/>).Если в документе не соблюдаются все правила синтаксиса XML, то он не является документом XML и, значит, не может быть обработан с помощью инструментов XML. Синтаксис XML очень точный и формальный. Все очень просто: либо в документе соблюдаются все правила синтаксиса XML, либо он не может быть распознан как документ XML.

Но некоторые приложения могут отказываться работать с абсолютно точно допустимыми документами. Такие приложения могут реализовывать синтаксис XML не полностью и, соответственно, быть неспособным распознать некоторые символьные сущности (например, î).

Проблема заключается в кажущейся простоте XML. Часто кажется, что проще и быстрее что-либо убрать, чем изучить какой-то новый компонент. Такой подход может работать в замкнутом цикле, когда приложение имеет дело с документом, созданный самим этим приложением. Но в промышленной эксплуатации, когда документ обрабатывается несколькими приложениями, этот подход уже вряд ли окажется успешным.

Решения и исправления

К счастью, всех этих проблем можно полностью избежать, используя парсер XML. Парсеры XML доступны во всех языках программирования (даже Cobol поддерживает XML), поэтому их безусловно стоит использовать.

У разработчика обычно есть две возможности: парсер XML или компонент преобразования. Если необходим контроль чтения документа XML низкого уровня, то лучше использовать парсер. Для целей данной статьи, неважно какой парсер вы будете использовать - DOM, JDOM, SAX, or StAX,- но только настоящий парсер XML является единственной гарантией того, что каждый документ XML будет правильно прочитан.

Если такой тщательный контроль, который обеспечивает парсер, не требуется, то компонент преобразования (такой как JAXB, Castor или Axis) может оказаться удобнее. Эти компоненты напрямую преобразуют тэги XML в объекты Java™. JAXB и Castor работают с документами в файлах, а Axis - с web-сервисами. Компоненты преобразования включают парсер XML, поэтому они полностью поддерживают синтаксис XML.

Хотя автор рекомендует использовать парсеры для чтения документов XML, он также признает, что этого можно избежать, если пользователь создает собственный способ для записи документов. Чтение документов XML - достаточно сложная задача, поскольку читающее программное обеспечение должно поддерживать полный синтаксис XML. Напротив, создание документов XML - это сравнительно более легкая процедура, поскольку можно использовать только часть синтаксических возможностей. Например, если пользователю не требуются атрибуты, то и не возникает необходимости в их поддержке; если не нужно многожество кодировок, то их также можно не поддерживать, и т.д.

Единственная опасность в этом подходе - это то, что нужно корректно передавать зарезервированные символы (см табл. 1). Особое внимание нужно обращать на символы сущностей (например, î) поскольку они зависят от кодировки документа (см. раздел "Проблемы кодировки" ниже).

Таблица 1. Зарезервированные символы

Символ 

Управляющая последовательность 

Примечания 

< <  
& &  
> >  
' ' Только в атрибутах, если символ " используется как разделитель
" " Только в атрибутах, если символ ' используется как разделитель
other &#unicode; Любой символ, не поддерживаемый данной кодировкой

Обычно достаточно простого цикла, аналогичного приведенному в листинге 1. Данную функцию можно применять более эффективно, но листинг 1 синтаксически корректен, если документ создается для потока UTF-8 или UTF-16 (в противном случае необходимо также передать некоторые символы за счет использования символьных сущностей).

Листинг 1. Применение стандартного алгоритма уклонения

                
// assumes UTF-8 or UTF-16 as encoding,
public String escape(String content)
{
   StringBuffer buffer = new StringBuffer();
   for(int i = 0;i < content.length();i++)
   {
      char c = content.charAt(i);
      if(c == '<')
         buffer.append("<");
      else if(c == '>')
         buffer.append(">");
      else if(c == '&')
         buffer.append("&");
      else if(c == '"')
         buffer.append(""");
      else if(c == '\'')
         buffer.append("'");
      else
         buffer.append(c);
   }
   return buffer.toString();
}

Некоторые разработчики предпочитают использовать секции CDATA вместо управляющей последовательности. CDATA - это механизм, который показывает, что часть документа может содержать незаменяемые зарезервированные символы. Например: <condition><![CDATA[a > 4]]></condition>. Я вернусь к секции CDATA в третьей статье серии, но на данный момент стоит сказать,что этот способ менее надежен, чем управляющая последовательность, т.к. одна секция CDATA не может включать другую такую же секцию.

В качестве более гибкого решения используйте преобразователь(transformer) -- загляните в мою подсказку -- "Инструмент XMLReader," на developerWorks.

Еще один способ исправлений

Представим ситуацию, что пользователю необходимо работать с приложением, которое не совсем соответствует синтаксису XML, и он не может убедить разработчика исправить это приложение.

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

Проблемы кодировки

Более серьезные проблемы могут возникнуть при использовании различных кодировок. Разработчики часто упускают из виду тот факт, что кодировки не ограничивают тот набор символов, который поддерживает XML. Любой документ XML поддерживает полный набор символов Unicode (16- или 32-битные символы в XML 1.1).

Использование кодировок в документе XML может сократить его размер, но при этом, благодаря наличию символьных сущностей, в нем могут оказаться не только символы Unicode. С помощью этих символов можно вставить любую букву из таблицы Unicode, даже если в документе используется наиболее строгая кодировка (US-ASCII, которая подходит только для четырех языков - английского, гавайского, латинского и суахили).

Это действительно проблема, поскольку если приложения Java или последняя версия DB2® могут поддерживать Unicode, то более ранние приложения почти не способны на это. Таким образом, если документ XML передается в "старое" приложение, придется столкнуться с Unicode. Соответственно, использование кодировок не является решением, поскольку, как показано выше, всегда можно избежать специальных символов за счет символьных сущностей.

Поскольку переписывание старого приложения редко используется как решение проблемы, необходим способ конвертирования, который превратит символы Unicode в набор, приемлемый для приложения: например, конвертирование "î" в обычную "i" (т.е. убирание диакритического знака). Большинство парсеров XML имеют возможности для обработки символов Unicode.

Проблемы пространства имен

Третий источник проблем, который обсуждается в настоящей статье, - это использование пространств имен XML.

Пространства имен были введены для управления словарями XML и предотвращения использования тэгов с одинаковыми именами. Часто два словаря могут использовать один и тот же тэг в разных контекстах. Например, в словаре сообщений могут быть тэги для темы, даты, отправителя, адресата и тела письма (см. листинг 2), а в словаре цифровых ресурсов - тэги для темы, даты, описания, камеры и номера кадра (см. листинг 3).

Листинг 2. Словарь сообщений

                
<envelope>
   <subject>Test memo</subject>
   <date>April 26, 2005</date>
   <from>jack@writeit.com</from>
   <to>john@xmli.com</to>
   <body>memo body goes here</body>
</envelope>

Листинг 3. Словарь цифровых ресурсов

                
<photo>
   <subject>Westlicht Museum of Camera and Photography, Vienna</subject>
   <date>April 25, 2005</date>
   <description>Lobby of the museum</description>
   <camera>Nikon D70</camera>
   <frame>5643</frame>
</photo>

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

Пространства имен XML превращают локальные имена в глобальные путем добавления глобального идентификатора к имени тэга. Для того чтобы гарантировать уникальность глобальных идентификаторов, они должны представлять собой URI (Uniform Resource Identifiers - универсальные идентификаторы ресурсов) (т.е. содержать имя домена, зарегистрированного для гарантии уникальности). Соответствующий пример приведен в листинге 4.

Листинг 4. Сочетание словарей

                
<env:envelope xmlns:env="http://psol.com/2005/env"
              xmlns:ph="http://psol.com/2005/photo">
   <env:subject>Latest photo</env:subject>
   <env:date>April 27, 2005</env:date>
   <env:from>jack@writeit.com</env:from>
   <env:to>john@xmli.com</env:to>
   <env:body>
      <ph:photo>
         <ph:subject>Westlicht Museum
             of Camera and Photography, Vienna</ph:subject>
         <ph:date>April 25, 2005</ph:date>
         <ph:description>Lobby of the museum</ph:description>
         <ph:camera>Nikon D70</ph:camera>
         <ph:frame>5643</ph:frame>
     </ph:photo></env:body>
</env:envelope>

Здесь есть два момента, которые требуют дополнительных пояснений:

  • URI - это идентификатор, а не адрес;
  • префикс - это не идентификатор.

URI и адреса

Хотя на практике большинство URI являются адресами (URL - Uniform Resource Locators - унифицированные указатели информационных ресурсов), в пространствах имен XML они используются только как идентификаторы. К сожалению, пространства имен не могут идентифицироваться так же, как пакеты Java. Например: com.psol.vocabulary вместо более неопределенного http://psol.com/vocabulary.

Поскольку URI являются идентификаторами, адреса могут не работать, т.е. выдавать ошибку "404 - Resource not found" - при попытке открыть их. Но они выполняют требуемые от них функции. И, вопреки широко распространенному заблуждению, URI пространств имен не указывают на схему XML Консорциума всемирной сети (World Wide Web Consortium - W3C).

Во-вторых, поскольку в этом контексте URI являются идентификаторами, приложение должно точно, до буквы, соответствовать их написанию. Было бы ошибкой использовать URI словаря XML для указания, например, на свой сервер. Например, URI для XSL выглядит следующим образом: http://www.w3.org/1999/XSL/Transform. Если пользователь работает в IBM, он не может превратить этот URI в, например, такой: http://www.ibm.com/1999/XSL/Transform. На самом деле изменения URI существующих словарей не допускаются вообще.

Когда я проводил занятия, посвященные XSLT, студенты часто недоумевали, почему у него/нее процессор XSLT не работает - на самом деле, причина была только в том, что XSLT URI не был воспроизведен правильно.

Первый вывод из вышеизложенного материала - необходимо воздерживаться от изменения пространств имен.Включение схемы версии в URI обычно не является хорошей идеей, поскольку это наверняка "сломает" приложения при выполнении обновлений (и да, я осознаю,что именно это и сделала W3C с SOAP).

Префиксы

Еще одна общая ошибка - путаница между префиксами и идентификаторами. Префикс не является идентификатором по той же причине, по какой им не может быть имя тэга: слишком велик риск, если два различных приложения используют одни и тот же префикс. Поэтому префиксы пространств имен прозрачны, и ими нельзя манипулировать в приложениях. Но для создателя документа XML вполне допустимо изменять префиксы в документе (например, для избежания конфликтов).

Примеры неправильного и правильного кодов приведены в листингах 5 и 6, соответственно.

Листинг 5. Некорректная проверка префиксов

                
startElement(String uri,String local,String qname,Attributes atts) 
{
   if(qname.equals("env:Envelope"))
      ;   // do something
}

Листинг 6. Корректная проверка URI пространства имени

                
startElement(String uri,String local,String qname,Attributes atts) 
{
   if(uri.equals("http://psol.com/2005/envelope")
      && local.equals("Envelope"))
      ;   // do something
}

Заключение

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


Страница сайта http://test.interface.ru
Оригинал находится по адресу http://test.interface.ru/home.asp?artId=6764