 |
от Андрей Христов(31-03-2002)
рейтинг (28)
[ добре ]
[ зле ]
Вариант за отпечатване
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 блока да се появява
низът ]]>, той
трябва да се замести с ]]>.
В допълнение на 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 последователен. Кой от
двата да се използва
трябва да избере програмиста, като балансира между скорост
и възможности.
<< Програмиране под Линукс. Среди за програмиране | Мозилла >>
|
 |