от Андрей Христов(31-03-2002)

рейтинг (28)   [ добре ]  [ зле ]

Printer Friendly Вариант за отпечатване

XML е език за описание, който е подмножество на SGML. XML много прилича по
външност  на HTML и това е следствие от това, че и HTML произлиза от
SGML. В началото HTML е бил език най-общо за визулизиране на информация,
като се има в предвид, че документът е имал структура от, която може да се
извлече информация. Под натиск от страна на различни големи фирми се добавят
нови и нови тагове и HTML се “изражда” в език за визуализация. От един съвременен
HTML документ на практика не може да бъде извлечена никаква полезна информация,
защото информацията не е представена като такава, а се цели нейното презентиране.
Тъй като не е възможно HTML отново да се вкара в някакви нормални рамки W3C(WWW
Consorcium) съставя спецификацията на нов език за описание на данни. Езикът
е трябвало да бъде такъв, че приложения на всякакви платформи е трябвало
да могат бързо и лесно да използват този формат.

XML като технология има най-различни приложения, като комуникация в хетерогенни
системи, съхранение на данни и др. Когато се говори за XML всъщност той е
обединяващото название на група от технологии, който използват XML формата,
в различни насоки. Примери са XSLT (eXtensible Stylesheet Language Transofrmations),
XPath, XLink, XQuery и др.

Примерен XML код :

<?xml version="1.0"?>
   <review>
       <genre>Action</genre>
       <title>X-Men</title>
       <cast>
           <person>Hugh
Jackman</person>
           <person>Patrick
Stewart</person>
           <person>Ian
McKellen</person>
           <person>Famke
Janssen</person>
       </cast>
       <director>Bryan Singer</director>
       <duration>104</duration>
       <year>2000</year>
       <body>Every once in a while,
Hollywood takes a comic-book hero, shoots him on celluloid, slaps in a few
whiz-bang special effects and stands back to see the reaction. Sometimes
the results are memorable (<title>Superman</title>, <title>Spiderman</title>,
<title>Flash Gordon</title>) and sometimes disastrous (<title>Spawn</title>,
<title>The Avengers</title>). Luckily, <title>X-Men</title>
falls into the former category - it's a clever, well-directed film that should
please both comic-book aficionados and their less well-read cousins.</body>

       <rating>4</rating>
</review>



Един XML документ може да е : добре форматиран и правилен. Добре форматиран
документ е този, който отговаря на XML спецификацията, за това как се описва.
Допълнително за документа могат да се зададат правила, използвайки които
да се провери дали той е правилен. Правилата описват подобие на контекстно
свободна граматика за структурата и данните в тялото на документа. Тези правила
се обединяват под названието document type definition (DTD) или XML Schema.
DTD е много мощно средство когато става въпрос за голям обем от документи
за дадена система. В този случай се използва често. По-рядко се използва,
когато имаме малко количество документи или проверката е изнесена в application
слой отгоре.
Всеки един XML документ трябва да започва със следната декларация :
<?xml version="1.0"?>
Тази декларация, може да съдържа информация за document encoding (примерно
UTF-8 или UTF-16). След тази декларация е възможно да има и други декларации.
Целият този блок наподобява <head> тага от HTML. DTD декларация изглежда
така :

<!DOCTYPE rootElement DTDLocation
[
entityDeclarations
]
>

Пример :
<BLOCKQUOTE>1. <!DOCTYPE review SYSTEM "http://www.somedomain.com/review.dtd">
2. <!DOCTYPE review SYSTEM “http://www.somedomain.com/review.dtd”
[
<!ENTITY html "Hypertext Markup Language">
<!ENTITY xml "Extensible Markup Language">
]
></BLOCKQUOTE>
В първият пример указваме, че DTD на тага review (root) се намира na адрес
: http://www.somedomain.com/review.dtd (чрез SYSTEM атрибута). !ENTITY е
аналог на #define html “Hypertext Markup Language” в езика C.
 
След последната декларация в пролога на документа се намира отварящ таг.
Този таг е root таг-а(ниво 0). На ниво 0 може да има само един елемент. Структурата
наподобява *nix файловата система.
 
Елемент има следната структура :
 
<element_name [attr1=”1”,,,]>data</element_name>
data е низ, който може да съсдържа други елементи и/или текст, и последващо
те(елементите) да съдържат други и т.н. Ако [data] e празен низ тогава има
съкратен синтаксис
<element_name [attr1=”1”,,,]/ >
Атрибутите са равнозначни на тагове, пример :
<person>
   <fname>John</fname>
   <lname>Smith</lname>
</person>
 
е равнозначно на
 
<person fname=”John” lname=”Smith”/ >
 
За програмният интерфейс те са еднозначни. Стойностите на атрибутите трябва
да са заградени в кавички(единични или двойни). Имената са case-sensitive
: fname е различно от Fname. Двата стила могат да се смесват и от програмиста
зависи, каква ще е структурат и къде е удачно да се използват атрибути и
къде вложени тагове.
 
Ето един по-голям пример :
<?xml version="1.0"?>
<recipe>
   <name>Chicken Tikka</name>
   <author>Anonymous</author>
   <date>1 June 1999</date>
   <ingredients>
       <item>
           <desc>Boneless
chicken breasts</desc>
           <quantity>2</quantity>
       </item>
       <item>
           <desc>Chopped
onions</desc>
           <quantity>2</quantity>
       </item>
       <item>
           <desc>Ginger</desc>
           <quantity>1
tsp</quantity>
       </item>
       <item>
           <desc>Garlic</desc>
           <quantity>1
tsp</quantity>
       </item>
       <item>
           <desc>Red
chili powder</desc>
           <quantity>1
tsp</quantity>
       </item>
       <item>
           <desc>Coriander
seeds</desc>
           <quantity>1
tsp</quantity>
       </item>
       <item>
           <desc>Lime
juice</desc>
           <quantity>2
tbsp</quantity>
       </item>
       <item>
           <desc>Butter</desc>
           <quantity>1
tbsp</quantity>
       </item>
   </ingredients>
   <servings>3</servings>
   <process>
       <step>Cut chicken into cubes,
wash and apply lime juice and salt</step>
       <step>Add ginger, garlic, chili,
coriander and lime juice in a separate bowl</step>
       <step>Mix well, and add chicken
to marinate for 3-4 hours</step>
       <step>Place chicken pieces on
skewers and barbeque</step>
       <step>Remove, apply butter, and
barbeque again until meat is tender</step>
       <step>Garnish with lemon and
chopped onions</step>
   </process>
</recipe>
 
Поради факта,че всеки таг има име, което описва данните, които съдържа в
себе си, това дава възможност един неорганизиран документ да бъде бъде превърнат
в структуриран от атомарни части. В примера по-горе  <author>
указва, че данните които се съдържат в него описват автора на рецептата,
докато <desc> и <quantity> се използват за съставките и респективно
за техните количества.
Текста, които се намира между отварящия и затварящия таг се нарича “character
data”. Въпреки, че CDATA може да съдържа букви и цифри, на някой символи
от азбуката на XML трябва бъдат представени по друг начин. Примерно знакът
амперсанд (&) се записва с &, което както може да се забележи
е !ENTITY. Има начин да се избегне това поведение и да се накара XML парсера
да не обработва текста. Използват се т.нар. CDATA блокове.
За CDATA блоковете експлицитно се указват, като несъдържащи markup. Тези
секции, могат да съдържат всичко : низове, числа, символи, дори йероглифи,
всички те ще бъдат пренебрегнати при обработката. CDATA блока започва така
:
<![CDATA[
и завършва така :
]]>
Данните, на секцията се намират между тези две последователности от символи.
Пример :
<?xml version="1.0"?>
<manual>
   <function>split(str, pattern)</function>
   <description>Split a string <param>str</param>
into component parts on the basis of <param>pattern</param>
   </description>
   <example>
   <![CDATA[
       <? split("apple, vanilla, orange",
","); ?>
   ]]>
   </example>
</manual>
 
<? и ?> са запазени за XML, но използвайки CDATA секция, можем да вградим
дори показният по-горе PHP код. Разбира се този малък файл може да бъде предаден
за изпълнение на PHP интепретатора, тъй като <?xml e непознат отварящ
таг за него. Ако се налага да в CDATA блока да се появява низът ]]>, той
трябва да се замести с ]]&gt.
В допълнение на cdata, XML също позволява авторите на документите да включват
специфични инструкции или команди, за обработващите приложения, в документите.
Тези инструкции наричани още “processing instructions” или PI, не са част
от “character data”, вместо това, когато XML парсера намери PI, го подава
на приложението грижещо се за обработка на PI от намерения клас(ако има такова
асоциирано, иначе го пренебрегва). Всяка PI съдържа “цел” (“target”), която
указва, към кое приложение да се пренасочат данните. Комбинацията от “цел”
и “данни” се обгражда с <? и ?> тагове, както е показано на примера
:
 
 
<?xml version="1.0"?>
<directory>
   <category>Online Shopping <?rating popular?></category>
   <url>http://www.amazon.com</url>
   <desc>Amazon.com, the planet's foremost e-tailer<?link_with_ad
?></desc> <url>http://www.cdnow.com</url>
   <desc>CDNow.com, for all your music</desc>
<url>http://www.bn.com</url>
   <desc>Barnes & Noble's online bookstore</desc>
</directory>
 
 
Тези данни ще бъдат използвани от XML приложение – примерно, първата PI ще
указва, че категорията е популярна, докато втората PI ще свърже описанието
с реклама. Както споменах в началото <?xml version=”1.0”?> също е PI.
 
 
Оттук показаното се вижда как XML позволява да се използват описателни тагове
за “подчертаване” на текст в документ. Авторите имат пълната свобода да създават
такива тагове, каквито са им нужни. И докато това показва, колко гъвкав като
концепция е XML, оказва се че това е нож с две остриета и внимателно трябва
да се борави. Интересен е въпросът какво ще станем ако в различни документи
имаме тагове с еднакви имена, но използвани в различен контекст.
 
За да бъде по-ясно ще използвам два различни по структура XML документа:
 
<?xml version="1.0"?>
<portfolio>
   <stock>Cisco Systems</stock>
   <stock>Nortel Networks</stock>
   <stock>eToys</stock>
   <stock>IBM</stock>
</portfolio>
 
и
 
<?xml version="1.0"?>
<inventory>
   <category>Mice</category>
   <item>Mouse C106</item>
   <vendor>Logitech</vendor>
   <stock>100</stock>
   <category>Handhelds</category>
   <item>Visor Deluxe</item>
   <vendor>HandSpring</vendor>
   <stock>23</stock>
   <category>MP3 players</category>
   <item>Nomad</item>
   <vendor>Creative</vendor>
   <stock>2</stock>
</inventory>
 
Първият документ описва портфолиото(продукти на кои производители предлата)на
една фирма. Вторият документ съдържа информация за наличностите в склада
на фирма за продажба на хардуер. Ако се опитаме да комбинираме двата документа
ще настъпи объркване, защото тага <stock> има различно значение в двата
документа. За да се избегне този проблем в XML е въведено понятието “пространства
на имена”. Xората занимвали се с C++ може да се се сблъсквали с този проблем
при множествено наследяване. Там проблема е решим като се укаже имплицитно,
коя данна/кой метод на родителя, се има в предвид чрез оператор :: . В XML
се използва : . Създаването на namespace е лесно. Синтаксиса е :
 
<elementName xmlns: prefix="namespaceURL">
 
В този случай, префикса е уникален низ използван за да се идентифицира “пространството”,
тое е link-нато за URL-а на пространството. “Пространство” обикновенно се
декларира, на нивото на root елемента, но това е препоръчително, но не задължително.
След като бъде декларирано едно “пространство”, то може да бъде използвано,
като пред всеки елемент от него се слага префикс – уникалният идентификатор.
Горните два документа преобразувани така, че да използват тази техника изглеждат
така :
 
<?xml version=”1.0”?>
<mytrades:portfolio
xmlns:mytrades="http://www.somedomain.com/namespaces/mytrades/">
   <mytrades:stock>Cisco Systems</mytrades:stock>
   <mytrades:stock>Nortel Networks</mytrades:stock>
   <mytrades:stock>eToys</mytrades:stock>
   <mytrades:stock>IBM</mytrades:stock>
</mytrades:portfolio>
 
и
<?xml version="1.0"?>
<toms_store:inventory xmlns:toms_store="http://www.toms_store.com/">
   <toms_store:category>Mice</toms_store:category>
   <toms_store:item>Mouse C106</toms_store:item>
   <toms_store:vendor>Logitech</toms_store:vendor>
   <toms_store:stock>100</toms_store:stock> <toms_store:category>Handhelds</toms_store:category>
   <toms_store:item>Visor Deluxe</toms_store:item>
   <toms_store:vendor>HandSpring</toms_store:vendor>
   <toms_store:stock>23</toms_store:stock>
   <toms_store:category>MP3 players</toms_store:category>
   <toms_store:item>Nomad</toms_store:item>
   <toms_store:vendor>Creative</toms_store:vendor>
   <toms_store:stock>2</toms_store:stock>
</toms_store:inventory>
 
URL-a на “пространството” се използва за да се укаже уникалност и за нищо
друго.
В случай, че един документ съдържа две или повече “пространства”, добавянето
на ново и префиксването на всеки елемент с идентификатор прави документа
определено труден за четене.
Малък пример :
<?xml version="1.0"?>
<me:person xmlns:me="http://www.mywebsite.com/">
   My name is <me:name>Huey</me:name>. I'm <me:age>seven</me:age>
years old,and I live in <me:address>Ducktown</me:address> with
   <rel:relationships xmlns:rel="http://www.mywebsite.com/relationships/">my
brothers <rel:name>Dewey</rel:name> and  
   <rel:name>Louie</rel:name></rel:relationships>.
</me:person>
В ситуации, като тази XML позволява да се укаже едно пространство, да бъде
по подразбиране. Така при 2 пространства, само елементите на едното ще бъдат
с префикси.
 
Също както HTML, XML позволява вмъкването на коментари. Коментарите се игнорират
от парсера и се използват за улесняване, прочитането да документа. Могат
да се поставят навсякъде в документа, заради по-горе споменатата причина.
Маркерите, указващи коментар са еквивалентни на тези в HTML. Te sa : <!--
и -->.
 
 <B>Използване на XML заедно с PHP.</B>
 
 
XML документи могат да се използват в PHP скриптове, като се изпозва някой
от двете достъпни разширения на PHP : SAX(Simple API for XML) и DOMXML. Целта
е обща, пътищата различни.
 
Първо ще разгледам SAX, а след това и DOMXML.
 
SAX парсера работи като се движи последователно от началото към краят на
XML документа и извиква специфични функции, когато срещне различни видове
тагове. Примерно, да се извика една функция, когато се срещне “старта” на
таг,друга при “краят” му и трета за обработка на съдържанието. Отговорност
на парсера е да процесира документа и да извика указаните преди началото
функции. След като един таг бъде обработен, парсера се премества на последващият
го ако има такъв или приключва работа, ако няма друг. PHP идва с парсера
expat.
Да предположим, че ни се налага да обработим следният XML документ, за да
използваме заложената в него информация :
 
<?xml version="1.0"?>
<library>
   <book>
       <title>Hannibal</title>
       <author>Thomas Harris</author>
       <genre>Suspense</genre>
       <pages>564</pages>
       <price>8.99</price>
       <rating>4</rating>
   </book>
   <book>
       <title>Run</title>
       <author>Douglas E. Winter</author>
       <genre>Thriller</genre>
       <pages>390</pages>
       <price>7.49</price>
       <rating>5</rating>
   </book>
   <book>
       <title>The Lord Of The Rings</title>
       <author>J. R. R. Tolkien</author>
       <genre>Fantasy</genre>
       <pages>3489</pages>
       <price>10.99</price>
       <rating>5</rating>
   </book>
</library>
 
Ще използваме следният скрипт за обработката :
 
 
<?php
// data file
$file = "library.xml"; // initialize parser
$xml_parser = xml_parser_create(); // set callback functions
xml_set_element_handler($xml_parser, "startElement", "endElement");
xml_set_character_data_handler($xml_parser, "characterData"); // open XML
file
if (!($fp = fopen($file, "r"))){
   die("Cannot locate XML data file: $file");
} // read and parse data
while ($data = fread($fp, 4096)){
// error handler
if (!xml_parse($xml_parser, $data, feof($fp))){
die(sprintf("XML error: %s at line %d",
xml_error_string(xml_get_error_code($xml_parser)),
xml_get_current_line_number($xml_parser)));
}
} // clean up
xml_parser_free($xml_parser);
?>
 
 <UL>
   <LI>xml_parser_create() се използва за да се инициализира parser и да
се асоциира с него handle(resource), който последващо да се използва при
извикването да други функции (аналогично на handle-ите за connection и db
в МySQL). След като е инициализиран парсера трябва да специфицираме callback
функции за различните видове тагове.</LI>
   <LI>xml_set_element_handler() се използва за да се зададе, кои ще са
callback фунциите, извиквани при намерен начален и краен таг. В примера по-горе
това са startElement() и "endElement().</LI>
 </UL>
 
Следващата стъпка е да отворим XML файла с име от $file, да го прочетем и
процесираме чрез xml_parse() фунцкията. Последнатаще извика нужната за обработка
функция всеки път, когато отчете наличието на специфичен таг. След като документа
е бил процесиран напълно трябва да се извика xml_parser_free() за да се освободи
паметта заемана от парсера. При наличие на грешка, може да се използват :
 
 <UL>
   <LI>xml_get_error_code()</LI>
   <LI>xml_get_current_line_number()</LI>
   <LI>xml_get_current_column_number()</LI>
   <LI>xml_get_current_byte_index()</LI>
 </UL>
за да имаме повече информация относно вида и мястополжението на грешката
в документа.
 
 
Ето и дефинициите на функциите startElement() и endElement().
<?php
// use this to keep track of which tag the parser is currently processing
$currentTag = "";
function startElement($parser, $name, $attrs) {
global $currentTag;
$currentTag = $name; // output opening HTML tags
switch ($name) {
case "BOOK": echo "<tr>"; break;
case "TITLE": echo "<td>"; break;
case "AUTHOR":echo "<td>"; break;
case "PRICE": echo "<td>"; break;
case "RATING": echo "<td>"; break;
default: break;
}
}
?>
 
 
<?php
function endElement($parser, $name) {
global $currentTag; // output closing HTML tags
switch ($name) {
case "BOOK": echo "</tr>"; break;
case "TITLE": echo "</td>"; break;
case "AUTHOR": echo "</td>"; break;
case "PRICE": echo "</td>"; break;
case "RATING": echo "</td>"; break;
default : break;
} // clear current tag variable
$currentTag = "";
}
?>
 
 
Всеки път, когато парсера намери отварящ таг, той извиква startElement()
с името на тага и атрибутите(ако има такива), като аргументи. След това startElement()
обработва тага и отпечатва респективният HTML.
 
<?
// process data between tags
function characterData($parser, $data) { global $currentTag;
// text ratings
$ratings = array("Words fail me!", "Terrible", "Bad", "Indifferent", "Good",
"Excellent");
// format the data
switch ($currentTag) {
case "TITLE":// italics for title
echo "<i>$data</i>"; break;
case "AUTHOR": echo $data; break;
case "PRICE": // add currency symbol for price
echo "$" . $data; break;
case "RATING": // get text rating
echo $ratings[$data]; break;
default: break;
}
}
?>
 
characterData() се извиква, когато парсера забележи данни между двойката
тагове. Трябва да се има в предвид, че функцията получава като аргумент само
данните, но не и името на тага. Поради това в примера се използва глобалната
променлива $currentTag. В зависимост от стойността $currentTag.
 
 <B>PHP с DOMXML</B>
Както и при работата със SAX, и при DOM се налага да се зареди целяит XML
документ в паметта. PHP предлага 3 функции за тази цел :
 <UL>
   <LI>xmldoc()</LI>
   <LI>xmltree()</LI>
   <LI>xmldocfile()</LI>
 </UL>
Първите 2 приемат като аргумент низ съдържащ XML данни и изграждат дървовидната
структура от този низ, докато xmldocfile() приема име на файл като аргумент.
 
<?php // create an XML-compliant string
$XMLDataString = ‘<?xml version="1.0"?’ . ‘>
<me><name>Joe Cool</name><age>24</age><sex>male</sex></me>";
// create a document object
$XMLDoc = xmldoc($XMLDataString);
// create a tree object
$XMLTree = xmltree($XMLDataString);
?>
или
 
<?php
// data file
$XMLFile = "me.xml";
// create a document object
$XMLDoc = xmldocfile($XMLFile);
?>
 
След като документа е бил зареден, множество от методи могат да бъдат ползвани
за да се обхожда дървото.  Има два начина за извикване : единият е чрез
обекта върнат от xmldoc() и xmldocfile(), т.е. чрез неговите методи, а другият
е чрез обикновенни функции, на които се подава като аргумент handle.
 
Примери:    
<?php
// data file
$file = "library.xml"; // create a document object
$dom = xmldocfile($file); // echo these values to see the object type
// get root node
$dom->root(); // get children under the root node as array
$dom->children();
?>
 
<?php
// data file
$file = "library.xml";
// create a document object
$dom = xmldocfile($file); // get XML version
echo $dom->version; // get XML encoding
echo $dom->encoding; // get whether standalone file
echo $dom->standalone; // get XML URL
echo $dom->url; // get XML character set
echo $dom->charset;
?>
 
Както се видя чрез използването на DOM или SAX могат да се постигнат еднакви
резултати. Разликата се състои в това,че DOM е по-бавен като метод, поради
факта, че цялото дърво трябва да се построи в началото, докато SAX е по-бърз,
защото извиква функция всеки път, когато намери таг. Също така при DOM достъпа
до елементи е пряк, докато при SAX последователен. Кой от двата да се използва
трябва да избере програмиста, като балансира между скорост и възможности.


<< Програмиране под Линукс. Среди за програмиране | Мозилла >>