XML (Extensible Markup Language) je vyznačovací jazyk, ktorý je medzi vývojármi obľúbený najmä vďaka jeho prehľadnosti a jednoduchosti. Najčastejšie sa používa na ukladanie konfigurácie programov a v jednoduchších aplikáciách dokáže poskytnúť rozumnú alternatívu k relačným databázovým systémom. Tvorí však aj základ protokolu SOAP a mnohých ďalších technológií ako napríklad AJAX či RSS. Procesu získavania údajov z XML štruktúr sa hovorí “parsovanie” a pre väčšinu programovacích jazykov existuje tzv. XML parser, čiže nástroj na čítanie XML štruktúr. V článku nájdete ukážky použitia XML parserov v jazykoch ANSI C, PHP, Perl, Java a Object Pascal.
Ak ste s jazykom XML ešte nepracovali, odporúčam vám preštudovať aspoň krátky tutoriál na webe w3schools.com. Oboznámite sa v ňom okrem iného so základnými pravidlami, ktorými by ste sa mali pri tvorbe XML štruktúr riadiť.
V ďalšej časti článku budem predpokladať, že súbor “tcpip.xml” obsahuje nasledovnú XML štruktúru:
<?xml version="1.0" encoding="utf-8" ?>
<model_tcpip>
<linkova_vrstva>
<protokol nazov="Ethernet" />
</linkova_vrstva>
<sietova_vrstva>
<protokol nazov="IP">Internet Protocol</protokol>
<protokol nazov="ICMP">Internet Control Message Protocol</protokol>
<protokol nazov="IGMP">Internet Group Management Protocol</protokol>
</sietova_vrstva>
<transportna_vrstva>
<protokol nazov="TCP">Transmission Control Protocol</protokol>
<protokol nazov="UDP">User Datagram Protocol</protokol>
</transportna_vrstva>
<aplikacna_vrstva>
<protokol sluzba="FTP" popis="File Transport Protocol" />
<protokol sluzba="SSH" popis="Secure SHell" />
<protokol sluzba="HTTP" popis="HyperText Transport Protocol" />
</aplikacna_vrstva>
</model_tcpip>
Vopred upozorňujem, že nasledujúce ukážky kódov obsahujú kvôli zvýšeniu prehľadnosti len minimálnu kontrolu chýb.
V príklade je použitá knižnica libxml2. Program sa kompiluje príkazom:
# gcc -Wall -O2 `xml2-config --cflags --libs` xmlparser.c -o xmlparser
Podstatnú časť programu už tradične zaberajú funkcie uvoľňujúce pamäť :)
#include <stdio.h>
#include <libxml/parser.h>
#include <libxml/tree.h>
int main(void) {
char *subor = "tcpip.xml";
xmlDoc *doc = NULL;
xmlNode *root_element = NULL;
xmlNode *current_node = NULL;
xmlNode *protokol = NULL;
xmlChar *atribut = NULL;
xmlChar *obsah = NULL;
LIBXML_TEST_VERSION
doc = xmlReadFile(subor, NULL, 0);
if (doc == NULL) {
printf("Parsovanie suboru %s neuspesne.n", subor);
return(1);
}
root_element = xmlDocGetRootElement(doc);
if (root_element == NULL) {
printf("Chyba pri parsovani XML suboru.n");
return(1);
}
for (current_node = root_element->children; current_node != NULL; current_node = current_node->next) {
if (current_node->type == XML_ELEMENT_NODE && !xmlStrcmp(current_node->name, (const xmlChar *) "linkova_vrstva")) {
printf("Linkova vrstvan");
for (protokol = current_node->children; protokol != NULL; protokol = protokol->next) {
atribut = xmlGetProp(protokol, (const xmlChar *) "nazov");
if (atribut) {
printf("t%sn",atribut);
}
xmlFree(atribut);
}
xmlFreeNode(protokol);
}
if (current_node->type == XML_ELEMENT_NODE && !xmlStrcmp(current_node->name, (const xmlChar *) "sietova_vrstva")) {
printf("Sietova vrstvan");
for (protokol = current_node->children; protokol != NULL; protokol = protokol->next) {
atribut = xmlGetProp(protokol, (const xmlChar *) "nazov");
obsah = xmlNodeGetContent(protokol);
if (atribut) {
printf("t%s - %sn", atribut, obsah);
}
xmlFree(obsah);
xmlFree(atribut);
}
xmlFreeNode(protokol);
}
if (current_node->type == XML_ELEMENT_NODE && !xmlStrcmp(current_node->name, (const xmlChar *) "transportna_vrstva")) {
printf("Transportna vrstvan");
for (protokol = current_node->children; protokol != NULL; protokol = protokol->next) {
atribut = xmlGetProp(protokol, (const xmlChar *) "nazov");
obsah = xmlNodeGetContent(protokol);
if (atribut) {
printf("t%s - %sn", atribut, obsah);
}
xmlFree(obsah);
xmlFree(atribut);
}
xmlFreeNode(protokol);
}
if (current_node->type == XML_ELEMENT_NODE && !xmlStrcmp(current_node->name, (const xmlChar *) "aplikacna_vrstva")) {
printf("Aplikacna vrstvan");
for (protokol = current_node->children; protokol != NULL; protokol = protokol->next) {
atribut = xmlGetProp(protokol, (const xmlChar *) "sluzba");
if (atribut) {
printf("t%s", atribut);
}
xmlFree(atribut);
atribut = xmlGetProp(protokol, (const xmlChar *) "popis");
if (atribut) {
printf(" - %sn", atribut);
}
xmlFree(atribut);
}
xmlFreeNode(protokol);
}
}
xmlFreeNode(current_node);
xmlFreeNode(root_element);
xmlFreeDoc(doc);
xmlCleanupParser();
return(0);
}
V príklade je použitý modul SimpleXML, ktorý je súčasťou PHP5. Myslím, že po porovnaní kódu s ostatnými príkladmi je každému hneď jasné, prečo je PHP tak populárne.
<?php
$subor = file_get_contents("./tcpip.xml");
$xml = new SimpleXMLElement($subor);
echo "nLinkova vrstvan";
foreach ($xml->linkova_vrstva->protokol as $protocol) {
echo "t".$protocol['nazov']."n";
}
echo "nSietova vrstvan";
foreach ($xml->sietova_vrstva->protokol as $protocol) {
echo "t".$protocol['nazov']." - ".$protocol."n";
}
echo "nTransportna vrstvan";
foreach ($xml->transportna_vrstva->protokol as $protocol) {
echo "t".$protocol['nazov']." - ".$protocol."n";
}
echo "nAplikacna vrstvan";
foreach ($xml->aplikacna_vrstva->protokol as $protocol) {
echo "t".$protocol['sluzba']." - ".$protocol['popis']."n";
}
?>
Osobne preferujem v perle parsovanie pomocou regulárnych výrazov, no v tomto prípade som pre názornosť použil modul XML::Simple. Pochopenie kódu vyžaduje znalosť asociatívnych polí (tzv. hash-ov).
#!/usr/bin/perl
use strict;
use XML::Simple;
my $xml = new XML::Simple;
my $data = $xml->XMLin("tcpip.xml", forcearray => 1);
print "Linkova vrstvan";
foreach my $protokol (@{$data->{linkova_vrstva}->[0]->{protokol}}) {
print "t".$protokol->{nazov}."n";
}
print "Sietova vrstvan";
foreach my $protokol (@{$data->{sietova_vrstva}->[0]->{protokol}}) {
print "t".$protokol->{nazov}." - ".$protokol->{content}."n";
}
print "Transportna vrstvan";
foreach my $protokol (@{$data->{transportna_vrstva}->[0]->{protokol}}) {
print "t".$protokol->{nazov}." - ".$protokol->{content}."n";
}
print "Aplikacna vrstvan";
foreach my $protokol (@{$data->{aplikacna_vrstva}->[0]->{protokol}}) {
print "t".$protokol->{sluzba}." - ".$protokol->{popis}."n";
}
I keď sa snažím, občas sa použitiu javy nedá vyhnúť. Program sa prekladá príkazom:
javac XMLparser.java
Bez Eclipse by sa mi tento príklad pravdepodobne nepodarilo “zlepiť”.
import javax.xml.parsers.*;
import org.w3c.dom.*;
public class XMLparser {
public static void main(String[] args) {
javax.xml.parsers.DocumentBuilderFactory dbf;
javax.xml.parsers.DocumentBuilder parser;
org.w3c.dom.Document document = null;
try {
dbf = DocumentBuilderFactory.newInstance();
dbf.setValidating(false);
parser = dbf.newDocumentBuilder();
document = parser.parse("tcpip.xml");
System.out.println("Linkova vrstva");
if (document.getElementsByTagName("linkova_vrstva").getLength() > 0) {
Element app_vrstva = (Element) document.getElementsByTagName("linkova_vrstva").item(0);
NodeList nodes = app_vrstva.getElementsByTagName("protokol");
for (int i = 0; i < nodes.getLength(); i++) {
Element protokol = (Element) nodes.item(i);
System.out.println(" "+protokol.getAttribute("nazov"));
}
}
System.out.println("Sietova vrstva");
if (document.getElementsByTagName("sietova_vrstva").getLength() > 0) {
Element app_vrstva = (Element) document.getElementsByTagName("sietova_vrstva").item(0);
NodeList nodes = app_vrstva.getElementsByTagName("protokol");
for (int i = 0; i < nodes.getLength(); i++) {
Element protokol = (Element) nodes.item(i);
System.out.println(" "+protokol.getAttribute("nazov")+" - "+protokol.getTextContent());
}
}
System.out.println("Transportna vrstva");
if (document.getElementsByTagName("transportna_vrstva").getLength() > 0) {
Element app_vrstva = (Element) document.getElementsByTagName("transportna_vrstva").item(0);
NodeList nodes = app_vrstva.getElementsByTagName("protokol");
for (int i = 0; i < nodes.getLength(); i++) {
Element protokol = (Element) nodes.item(i);
System.out.println(" "+protokol.getAttribute("nazov")+" - "+protokol.getTextContent());
}
}
System.out.println("Aplikacna vrstva");
if (document.getElementsByTagName("aplikacna_vrstva").getLength() > 0) {
Element app_vrstva = (Element) document.getElementsByTagName("aplikacna_vrstva").item(0);
NodeList nodes = app_vrstva.getElementsByTagName("protokol");
for (int i = 0; i < nodes.getLength(); i++) {
Element protokol = (Element) nodes.item(i);
System.out.println(" "+protokol.getAttribute("sluzba")+" - "+protokol.getAttribute("popis"));
}
}
} catch (Exception e) {
System.out.println("Chyba pri parsovani XML suboru - "+e);
}
}
}
Uvoľnenie Turbo Delphi bolo pre mňa udalosťou roka 2006. Borland je opäť v hre.
program xmlparser;
{$APPTYPE CONSOLE}
uses
SysUtils, Activex, XMLDoc, XMLIntf;
var
XMLDocument: IXMLDocument;
XMLroot, XMLvrstva, XMLproto : IXMLNode;
i : integer;
begin
CoInitialize(nil);
XMLDocument := TXMLDocument.Create(nil);
// Pri pouziti VCL komponenty TXMLDocument nie je treba
// pouzit predchadzajuce 2 riadky
try
XMLDocument.XML.LoadFromFile('tcpip.xml');
XMLDocument.Active := True;
XMLroot := XMLDocument.DocumentElement;
writeln('Linkova vrstva');
XMLvrstva := XMLroot.ChildNodes.Nodes['linkova_vrstva'];
if (Assigned(XMLvrstva)) then
begin
for i := 0 to XMLvrstva.ChildNodes.Count - 1 do
begin
XMLproto := XMLvrstva.ChildNodes[i];
writeln(' '+XMLproto.Attributes['nazov']);
end;
end;
writeln('Sietova vrstva');
XMLvrstva := nil;
XMLvrstva := XMLroot.ChildNodes.Nodes['sietova_vrstva'];
if (Assigned(XMLvrstva)) then
begin
for i := 0 to XMLvrstva.ChildNodes.Count - 1 do
begin
XMLproto := XMLvrstva.ChildNodes[i];
writeln(' '+XMLproto.Attributes['nazov']+' - '+XMLproto.NodeValue);
end;
end;
writeln('Transportna vrstva');
XMLvrstva := nil;
XMLvrstva := XMLroot.ChildNodes.Nodes['transportna_vrstva'];
if (Assigned(XMLvrstva)) then
begin
for i := 0 to XMLvrstva.ChildNodes.Count - 1 do
begin
XMLproto := XMLvrstva.ChildNodes[i];
writeln(' '+XMLproto.Attributes['nazov']+' - '+XMLproto.NodeValue);
end;
end;
writeln('Aplikacna vrstva');
XMLvrstva := nil;
XMLvrstva := XMLroot.ChildNodes.Nodes['aplikacna_vrstva'];
if (Assigned(XMLvrstva)) then
begin
for i := 0 to XMLvrstva.ChildNodes.Count - 1 do
begin
XMLproto := XMLvrstva.ChildNodes[i];
writeln(' '+XMLproto.Attributes['sluzba']+' - '+XMLproto.Attributes['popis']);
end;
end;
except
writeln('Pri parsovani XML suboru sa vyskytla chyba.');
end;
XMLDocument.Active := False;
XMLDocument := nil;
readln;
end.
Neostáva mi iné, než odporučiť malú rozcvičku, pretože programovať sa človek učí programovaním. Skúste teda vo svojom obľúbenom jazyku napísať napríklad vlastnú RSS čítačku.
Zdravim, neviem preco mi vypisuje pri pouziti kodu takuto hlasku??
Fatal error: Call to undefined function: simplexml_load_file() in /var/www/vhosts/len-in.sk/http
Moznosti je viacero. Mozete mat starsiu verziu interpreta PHP, ktory tuto funkciu este nepozna alebo je PHP skompilovane bez SimpleXML modulu. Pricinu by som hladal v logoch web servera resp. vo vypise funkcie phpinfo().
Zdravim. Co sa tyka parserov pre formaty txt,doc, rtf alebo html mozes dako poradit? Robim program v Delphi pre ukladanie dokumentov do databazy SQL a hodilo by sa mi parsovanie aby som mohol ulozit cisto len text do DB pre fulltextove vyhladavanie. Vedel by si mi s tym dako pomoct?
Nechapem celkom preco spominas parser na txt a html, pretoze tieto formaty su textove. Co sa tyka doc a rtf tak myslim, ze v Delphi mozes pouzit ActiveX alebo COM komponenty MS Office a s nimi ulozit dokument ako plaintext.
mam problem, mam xml subor
<?xml version=”1.0″ encoding=”ISO-8859-2″?>
a ked ho rozparsujem
XMLproto.Attributes['popis']
tak mi nezobrazuje spravne diakritiku…
nasiel som aj nieco taketo
XMLDocument.Encoding := ‘ISO-8859-2′;
avsak vobec nic sa nestalo… nevie mi niekto poradit?
vdaka
DELPHI 6.0
Super prispevok, dik
neviem ci po troch rokoch je to este aktualne, ale zaujimalo by ma rozparsovanie neznameho suboru, teda bez predpokladanej struktury (stači v DELPHI), a ci je to rychlejsie ako riesenie pomocou transformacii