Spis treści

Opis

Projekt zakończony

Wojciech Szymański wojtek.szym@gmail.com

Omówić metody parsowania, generowania i przetwarzania XML/SGML w Prologu. Należy również zająć się reprezentacjami pochodnymi opartymi na XML, np. RDF.

Porównać dostępne rozwiązania i implementacje, np:

Wsparcie dla

Spotkania

Spotkania

Sprawozdanie

Wstęp - ogólne informacje

Celem projektu było omówienie metod parsowania, generowania oraz przetwarzania XML/SGML w Prologu. Należało porównać i przetestować dostępne rozwiązania oraz sprawdzić czy posiadają wsparcie dla DOM, XSD, XSL oraz namespaces. Istotną częścią projektu była implementacja własnego API przeznaczonego do tworzenia dokumentów XML w SWI-Prolog. Okazało się bowiem, że dostępne rozwiązania posiadają przede wszystkim dobre mechanizmy parsowania dokumentu (tworzenia postaci prologowej) oraz jego przetwarzania - zamiany postaci prologowej na dokument XML, natomiast nie posiadają wsparcia dla generowania XML opartego o standard DOM.

Parsowanie i przetwarzanie prostych dokumentów XML w prologu

Istnieje wiele implementacji prologu posiadających wsparcie dla generowania i przetwarzania dokumentów XML. Dostępne są zarówno wersje płatne, które można przetestować (np. 30-dniowe wersje) oraz wersje bezpłatne.

W celu przetestowania parsowania oraz przetwarzania plików XML przygotowano następujący, prosty plik XML:

<CD>
    <TITLE>Empire Burlesque</TITLE>
    <ARTIST>Bob Dylan</ARTIST>
    <COUNTRY>USA</COUNTRY>
    <COMPANY>Columbia</COMPANY>
    <PRICE>10.90</PRICE>
    <YEAR>1985</YEAR>
    <TITLE>Hide your heart</TITLE>
    <ARTIST>Bonnie Tylor</ARTIST>
    <COUNTRY>UK</COUNTRY>
    <COMPANY>CBS Records</COMPANY>
    <PRICE>9.90</PRICE>
    <YEAR>1988</YEAR>
    <TITLE>Greatest Hits</TITLE>
    <ARTIST>Dolly Parton</ARTIST>
    <COUNTRY>USA</COUNTRY>
    <COMPANY>RCA</COMPANY>
    <PRICE>9.90</PRICE>
    <YEAR>1982</YEAR>
    <TITLE>Still got the blues</TITLE>
    <ARTIST>Gary More</ARTIST>
    <COUNTRY>UK</COUNTRY>
    <COMPANY>Virgin redords</COMPANY>
    <PRICE>10.20</PRICE>
    <YEAR>1990</YEAR>

Przetestowano następujące implementacje prologu: SWI-Prolog, BProlog, Amzi! Prolog, MINERVA, JIProlog oraz YAP Prolog.

SWI-Prolog

SWI-Prolog posiada pakiet sgml2pl - SWI-Prolog SGML/XML parser. Pakiet ten posiada możliwości parsowania dokumentów XML wraz z namespaces oraz plików XML opartych o model DOM. Podstawowym predykatem służącym do parsowania jest: load_xml_file(+File,-ListOfContents). Natomiast predykat służący do przetworzenia postaci prologowej na dokument XML ma postać: xml_write(+Stream, +Term, +Options).

Po wpisaniu w prologu:

?- load_xml_file('cd.xml',T).

otrzymano:

T = [element('CD', [], [
element('TITLE', [], ['Empire Burlesque']), 
element('ARTIST', [], ['Bob Dylan']), 
element('COUNTRY', [], ['USA']), 
element('COMPANY', [], ['Columbia']), 
element('PRICE', [], ['10.90']), 
element('YEAR', [], [...]), 
element(..., ..., ...)|...])]

W celu przetworzenia z powrotem postaci prologowej do postaci XML wpisujemy:

?- load_xml_file('cd.xml',L),tell('cd1.xml'),open('cd1.xml',write,S),xml_write(S,L,[]),told,close(S).

i otrzymujemy dokument XML o nazwie cd1 stanowiący kopię dokumentu cd.xml (Dokument został sparsowany, a następnie zapisany do pliku o nowej nazwie ).

BProlog

Implementacja BProlog posiada bibliotekę o nazwie xml.pl. Podstawowym predykatem do parsowania plików XML jest: xml2pl(Input,Output). Jako parametr Input podajemy plik XML, a jako Output - plik, w którym chcemy zapisać prologową strukturę XML. Poniżej przedstawiono kroki w celu otrzymania struktury XML:

  1. Komenda cd(Dir) ustala ścieżkę, w której pracujemy, np. cd('C:/BProlog/Tools'). Tam muszą więc znajdować sie zarówno pilik XML jak i plik biblioteki 'xml.pl'.
  2. Komenda cl(File) kompiluje i ładuje program, np. cl('xml.pl') kompiluje i ładuje bibliotekę 'xml.pl'.
  3. Dalej posługujemy się predykatem, np. xml2pl('cd.xml','nowy.pl') tworzy plik nowy.pl zawierający postać prologową pliku cd.xml.

Poniżej przedstawiono wygenerowaną postać w prologu:

xml( [version="1.0", encoding="ISO-8859-1", standalone="no"],
	[
	doctype( 'CD', system( "cd.dtd" ) ),
	element( 'CD',
		[],
		[
		element( 'TITLE',[],[pcdata("Empire Burlesque")] ),
		element( 'ARTIST',[],[pcdata("Bob Dylan")] ),
		element( 'COUNTRY',[],[pcdata("USA")] ),
		element( 'COMPANY',[],[pcdata("Columbia")] ),
		element( 'PRICE',[],[pcdata("10.90")] ),
		element( 'YEAR',[],[pcdata("1985")] ),
		element( 'TITLE',[],[pcdata("Hide your heart")] ),
		element( 'ARTIST',[],[pcdata("Bonnie Tylor")] ),
		element( 'COUNTRY',[],[pcdata("UK")] ),
		element( 'COMPANY',[],[pcdata("CBS Records")] ),
		element( 'PRICE',[],[pcdata("9.90")] ),
		element( 'YEAR',[],[pcdata("1988")] ),
		element( 'TITLE',[],[pcdata("Greatest Hits")] ),
		element( 'ARTIST',[],[pcdata("Dolly Parton")] ),
		element( 'COUNTRY',[],[pcdata("USA")] ),
		element( 'COMPANY',[],[pcdata("RCA")] ),
		element( 'PRICE',[],[pcdata("9.90")] ),
		element( 'YEAR',[],[pcdata("1982")] ),
		element( 'TITLE',[],[pcdata("Still got the blues")] ),
		element( 'ARTIST',[],[pcdata("Gary More")] ),
		element( 'COUNTRY',[],[pcdata("UK")] ),
		element( 'COMPANY',[],[pcdata("Virgin redords")] ),
		element( 'PRICE',[],[pcdata("10.20")] ),
		element( 'YEAR',[],[pcdata("1990")] )
		] )
	] ).

Do przetworzenia postaci prologowej na dokument XML służy predykat: pl2xml(Document,Codes).

Amzi! Prolog

Wersja prologu Amzi! jest możliwa do ściągnięcia jako wersja „Free (Never Expires)”. Za inne wersje trzeba płacić. Amzi! Prolog posiada bibliotekę obsługującą pliki XML:XML.pro. Niestety nie udało sie skorzystać z wyżej wymienionej biblioteki.

MINERVA

Minerva jest licencjonowanym produktem firmy IF Computer Japan. Jest możliwe posiadanie licencji ewaluacyjnej (wolnej od opłaty) ważnej na okres 2 miesięcy. Minerva może być nazwana jako Java Prolog ponieważ dostarcza funkcjonalności Prologu jako klasa Javy.

Podstawowymi predykatami związanymi z parsowaniem i przetwarzaniem dokumentów XML są:

Niestety nie przetestowano użycia wyżej wymienionych predykatów. Patrząc jednak na przykłady udostępnione na stronie domowej MINERVY funkcjonalności zaproponowane przez ten produkt nie różnią się od tych użytych np. w SWI-Prolog.

Przykładem może być wygenerowana prologowa struktura dokumentu XML po sparsowaniu dokumentu korzystając z predykatu read_xml(XmlStream, XmlTerm).

document([xml([version = '1.0'])],
  element(addressbook,[],[
    element(address,[telno = '1234567'],[
      element(name,[],[chardata('John')]),
      element(age,[],[chardata('35')])]),
    element(address,[telno = '3456789',email = 'henriette@ifcomputer.com'],[
      element(name,[],[chardata('Henriette')]),
      element(age,[],[chardata('27')])])]),[])

JIProlog

JIProlog jest bezpłatnym interpreterem prologu integrującym języki Java i Prolog. Podczas działania wyświetla się tylko napis informujący użytkownika, że posiada wersje shareware unregistered. JIProlog posiada bibliotekę jipxxml do obsługi plików XML zgodnych z modelem DOM. Podstawowymi predykatami służącymi do parsowania i przetwarzania plików XML są

Aby sparsować dokument XML należy kolejno:

  1. Załadować bibliotekę do obsługi XML - load_library('jipxxml.jar').
  2. Załadować bibliotekę do obsługi input/output plików - load_library('jipxio.jar').
  3. Otworzyć dokument XML - see('cd.xml').
  4. Użyć predykatu: xml_read_document(X)

Poniżej przedstawiono wygenerowany plik w postaci prologowej:

X = xml_document([[version = 1.0, encoding = UTF-8], 
xml_doctype(CD, [SYSTEM = cd.dtd], [])], 
xml_element(CD, [], 
[xml_element(TITLE, [], [xml_text(Empire Burlesque)]), 
xml_element(ARTIST, [], [xml_text(Bob Dylan)]), 
xml_element(COUNTRY, [], [xml_text(USA)]), 
xml_element(COMPANY, [], [xml_text(Columbia)]), 
xml_element(PRICE, [], [xml_text(10.90)]), 
xml_element(YEAR, [], [xml_text(1985)]), 
xml_element(TITLE, [], [xml_text(Hide your heart)]), 
xml_element(ARTIST, [], [xml_text(Bonnie Tylor)]), 
xml_element(COUNTRY, [], [xml_text(UK)]), 
xml_element(COMPANY, [], [xml_text(CBS Records)]), 
xml_element(PRICE, [], [xml_text(9.90)]), 
xml_element(YEAR, [], [xml_text(1988)]), 
xml_element(TITLE, [], [xml_text(Greatest Hits)]), 
xml_element(ARTIST, [], [xml_text(Dolly Parton)]), 
xml_element(COUNTRY, [], [xml_text(USA)]), 
xml_element(COMPANY, [], [xml_text(RCA)]), 
xml_element(PRICE, [], [xml_text(9.90)]), 
xml_element(YEAR, [], [xml_text(1982)]), 
xml_element(TITLE, [], [xml_text(Still got the blues)]), 
xml_element(ARTIST, [], [xml_text(Gary More)]), 
xml_element(COUNTRY, [], [xml_text(UK)]), 
xml_element(COMPANY, [], [xml_text(Virgin redords)]), 
xml_element(PRICE, [], [xml_text(10.20)]), 
xml_element(YEAR, [], [xml_text(1990)])
]))

JIProlog posiada również szereg predykatów związanych z budowaniem plików XML zgodnych z modelem DOM. Najważniejszymi predykatami są:

Poniżej zamieszczono przykład budowania prostego pliku XML z użyciem wyżej wymienionych predykatów:

create_xml_file(X):-
   tell(X),
   % create root element
   xml_element('music_shop', [], [], Root),
   % create child 1n
   xml_element('cd', [], [], Cd1),
   %create attributes
   xml_attribute('artist', 'Bob Dylan', Att1),
   xml_attribute('title', 'Empire Burlesque', Att2),
   xml_attribute('price', '10.90', Att3),
   % append attributes
   xml_append_attribute(Att1, Cd1, Cd11),
   xml_append_attribute(Att2, Cd11, Cd12),
   xml_append_attribute(Att3, Cd12, Cd13),
   % create child 
   xml_text('Music CD', Title1),
   % append child
   xml_append_child(Title1, Cd13, Cd14),
   % append children to root
   xml_append_child(Cd14, Root, Root1),
   % create document
   xml_document('1.0', [], [], Root1, Doc),
   xml_write_document(Doc),
   told.

Z powyższego przykładu otrzymano następujący plik XML: Plik XML

YAP Prolog

Wersja YAP Prolog jest bezpłatna w użyciu w środowiskach akademickich. YAP Prolog dysponuje biblioteką posiadającą możliwości związane z parsowaniem i przetwarzaniem plików XML/HTML The PiLLoW Web Programming Library .

Wersje tej biblioteki są dostępne zarówno dla YAP Prolog jak i dla SWI Prolog oraz Ciao Prolog.

Jeśli chodzi o predykaty związane z XML jest dostępny: xml2terms(String,Terms)xml2terms

Predykat ten wykorzystuje się na dwa sposoby:

  1. translacja XML-term'u do kodu XML
  2. translacja kodu XML do strukturalnego termu XML'owego

Porównanie prologowych postaci dokumentu XML

Porównanie przeprowadzono dla pliku XML wygenerowanego z systemu VARDA-M_1.Thermostat

Do porównania zdecydowano się wykorzystać następujące wersje prologu.

Wersje te nie posiadają żadnych ograniczeń w użyciu.

Otrzymano następujące wyniki:

SWI-Prolog

Plik XML po sparsowaniu w SWI Prologu za pomocą predykatu load_xml_file('therm.xml',X) jest w postaci listy, która składa się z zagnieżdżonych struktur typu element(Node_Name, [Value], [child_element]), gdzie argumentami są:

Poniżej przedstawiono sparsowany plik XML w SWI-Prolog:

X = [element(hml, [], [
    , element(type_set, [], []), 
    , element(attribute_set, [], [
        , element(att, [name=Thermostat, id=att_0, value=single, class=ro], []), 
        , element(att, [name=Time, id=att_1, value=single, class=ro], []), 
        , element(att, [name=Temperature, id=att_2, value=single, class=ro], []), 
        , element(att, [name=Date, id=att_3, value=single, class=ro], []), 
        , element(att, [name=Hour, id=att_4, value=single, class=ro], []), 
        , element(att, [name=season, id=att_5, value=single, class=ro], []), 
        , element(att, [name=operation, id=att_6, value=single, class=ro], []), 
        , element(att, [name=thermostat_settings, id=att_7, value=single, class=ro], []), 
        , element(att, [name=day, id=att_8, value=single, class=ro], []), 
        , element(att, [name=month, id=att_9, value=single, class=ro], []), 
        , element(att, [name=today, id=att_10, value=single, class=ro], []), 
        , element(att, [name=hour, id=att_11, value=single, class=ro], []), 
    ]), 
    , element(property_set, [], [
        , element(property, [id=prp_0], [
            , element(attref, [ref=att_0], []), 
        ]), 
        , element(property, [id=prp_1], [
            , element(attref, [ref=att_1], []), 
            , element(attref, [ref=att_2], []), 
        ]), 
        , element(property, [id=prp_2], [
            , element(attref, [ref=att_1], []), 
        ]), 
        , element(property, [id=prp_3], [
            , element(attref, [ref=att_2], []), 
        ]), 
	, element(property, [id=prp_4], [
            , element(attref, [ref=att_3], []), 
            , element(attref, [ref=att_4], []), 
            , element(attref, [ref=att_5], []), 
            , element(attref, [ref=att_6], []), 
        ]), 
        , element(property, [id=prp_5], [
            , element(attref, [ref=att_3], []), 
            , element(attref, [ref=att_4], []), 
        ]), 
        , element(property, [id=prp_6], [
            , element(attref, [ref=att_5], []), 
            , element(attref, [ref=att_6], []), 
        ]), 
        , element(property, [id=prp_7], [
            , element(attref, [ref=att_7], []), 
        ]), 
        , element(property, [id=prp_8], [
            , element(attref, [ref=att_5], []), 
        ]), 
        , element(property, [id=prp_9], [
            , element(attref, [ref=att_6], []), 
        ]), 
        , element(property, [id=prp_10], [
            , element(attref, [ref=att_3], []), 
        ]), 
        , element(property, [id=prp_11], [
            , element(attref, [ref=att_4], []), 
        ]), 
        , element(property, [id=prp_12], [
            , element(attref, [ref=att_8], []), 
            , element(attref, [ref=att_9], []), 
            , element(attref, [ref=att_10], []), 
        ]), 
        , element(property, [id=prp_13], [
            , element(attref, [ref=att_9], []), 
        ]), 
	, element(property, [id=prp_14], [
            , element(attref, [ref=att_8], []), 
            , element(attref, [ref=att_10], []), 
        ]), 
        , element(property, [id=prp_15], [
            , element(attref, [ref=att_8], []), 
        ]), 
        , element(property, [id=prp_16], [
            , element(attref, [ref=att_10], []), 
        ]), 
        , element(property, [id=prp_17], [
            , element(attref, [ref=att_11], []), 
        ]), 
    ]), 
    , element(tph, [], [
        , element(trans, [src=prp_0, dst=prp_1], []), 
        , element(trans, [src=prp_1, dst=prp_2], []), 
        , element(trans, [src=prp_1, dst=prp_3], []), 
        , element(trans, [src=prp_2, dst=prp_4], []), 
        , element(trans, [src=prp_4, dst=prp_5], []), 
        , element(trans, [src=prp_4, dst=prp_6], []), 
        , element(trans, [src=prp_3, dst=prp_7], []), 
        , element(trans, [src=prp_6, dst=prp_8], []), 
        , element(trans, [src=prp_6, dst=prp_9], []), 
        , element(trans, [src=prp_5, dst=prp_10], []), 
        , element(trans, [src=prp_5, dst=prp_11], []), 
        , element(trans, [src=prp_10, dst=prp_12], []), 
        , element(trans, [src=prp_12, dst=prp_13], []), 
        , element(trans, [src=prp_12, dst=prp_14], []), 
        , element(trans, [src=prp_14, dst=prp_15], []), 
        , element(trans, [src=prp_14, dst=prp_16], []), 
        , element(trans, [src=prp_11, dst=prp_17], []), 
    ]), 
    , element(ard, [], [
        , element(dep, [independent=prp_8, dependent=prp_7], []), 
        , element(dep, [independent=prp_9, dependent=prp_7], []), 
        , element(dep, [independent=prp_13, dependent=prp_8], []), 
        , element(dep, [independent=prp_15, dependent=prp_16], []), 
        , element(dep, [independent=prp_16, dependent=prp_9], []), 
        , element(dep, [independent=prp_17, dependent=prp_9], []), 
    ]), 
])]

BProlog

Dokumenty XML po sparsowaniu w BProlog'u posiadają strukturę w postaci termu:

xml([version, encoding],[ element(…)])

Argumentami są:

Poniżej przedstawiono sparsowany plik XML w BProlog:

 

JIProlog

Dokument XML po sparsowaniu w JIProlog jest przedstawiony w postaci zagnieżdżonych termów zaczynając od xml_document i idąc wgłąb xml_element, xml_attribute oraz xml_text.

Charakterystyczne dla postaci prologowej są:

Poniżej przedstawiono sparsowany plik XML w JIProlog:

 

Implementacja API w SWI-Prolog

Ogólnodostępne wersje prologu nie posiadają mechanizmów do generowania dokumentów XML-DOM. Jedynie JIProlog dysponuje takimi możliwościami. Predykaty pozwalające na budowę dokumentu XML w JIProlog zostały omówione w sekcji (JIprolog) W związku z tym postanowiono zaimplementować własne API do budowania plików XML. Jako wersję prologu przyjęto SWI-Prolog. Zaimplementowano następujące główne predykaty:

  1. create_root/2 - tworzenie korzenia dokumentu XML
  2. app_child/4 - dodawanie dzieci
  3. add_attribute/3 - dodawanie atrybutów

Plik zawierający pełne API dostępny jest do pobrania tutaj

W przyszłości funkcjonalności API można dalej rozszerzać, np. o predykaty umożliwiające usuwanie dzieci, usuwanie atrybutów, czy też zmianę nazwy atrybutu bądź jego wartości.

Przykład użycia API

W celu prezentacji działania API przygotowano przykład, w którym pokazane zostało generowanie dokumentu XML (budowanie dokumentu podobnego do pliku pochodzącego z systemu VARDA).

Budowanie dokumentu XML rozpoczyna się od stworzenia korzenia.

Tworzenie korzenia

Korzen dokumentu xml tworzymy wykorzystując predykat:

create_root(Name,ID_name).

Jako parametry podajemy kolejno:

Po użyciu tego predykatu pojawi się nam główny element struktury prologowej stanowiący korzeń dokumentu xml.

Przykład:

?- create_root(hml,hml).
 
Yes
?- listing(element).
 
:- dynamic element/3.
 
element(hml, [id=hml_0], []).

Po stworzeniu korzenia dokumentu XML przechodzimy do dodawania kolejnych dzieci.

Dodawanie dzieci

Dodawanie dzieci do korzenia dokumentu lub do istniejących rodziców wykonuje się za pomocą predykatu:

app_child(Parent,P_id,Child,ChildIdName).

W predykacie tym jako argumenty podajemy kolejno:

:!::!:UWAGA:!::!:

Kiedy podajemy id rodzica, do którego chcemy przypisać dziecko, podajemy pełny tekst, np. id=prp_1.

Dodanie dziecka o nazwie type_set i id typ_s do rodzica hml (korzeń) przedstawia poniższy kod:

?- app_child(hml,_,type_set,typ_s).

Powstaje następująca struktura, korzeń hml ma na liście dziecko type_set z id o podanym prefiksie i przydzielonym numerze.

element(hml, [id=hml_0], [element(type_set, [id=typ_s_0], [])]).

W podobny sposób dodajemy dzieci:

W celu dodania za jednym razem większej ilości dzieci używamy predykatu pochodnego:

app_child(Parent,P_id,Child,ChildIdName,How_many).

Dodatkowym argumentem jest How_many, który oznacza ilość dodawanych dzieci.

Jest to przydatne np, podczas dodawania wielu dzieci att do rodzica attribute_set. Po użyciu predykatu:

app_child(attribute_set,_,att,att,12).

zostanie dodanych dwanaście dzieci do rodzica attribute_set.

Podobnie dodajemy:

Następnie pozostaje tylko dodanie odpowiednich dzieci attref do odpowiednich rodziców property. Tu ponownie wykorzystujemy wyżej wymieniany predykat, ale już z podaniem ID rodzica. Przykładowo, po wpisaniu:

 ?- app_child(property,id=prp_1,attref,att_r,2)

dodane zostaną dwoje dzieci attref z id o prefiksie att_r_ do rodzica property o id id=prp_1.


:!::!:UWAGA:!::!:

Kiedy podajemy id rodzica, do którego chcemy przypisać dziecko, podajemy pełny tekst, np. id=prp_1.


W ten sposób stworzono cały szkielet dokumentu XML o zadanym korzeniu oraz zadanych dzieciach. Każdy element posiada własne, unikalne id. Ostatnią rzeczą będzie dodawanie odpowiednich atrybutów.

Dodawanie atrybutów

Atrybuty do wybranego elementu dodaje się przy użyciu predykatu,

add_attribute(Parent,P_id,Attr).

gdzie jako argument podajemy kolejno:


:!::!:UWAGA:!::!:

Kiedy podajemy id elementu, do którego chcemy dodać atrybut, podajemy pełny tekst, np. id=att_0.

Podobnie jest w przypadku podawania atrybutu. Podajemy pełny tekst zawierający znak „=” , np. name=thermostat


Przykładowo użycie

?- add_attribute(att,id=att_0,name=thermostat).

spowoduje dodanie atrybutu name=thermostat do elementu att o id id=att_0.

W ten sam sposób możemy dodać każdy atrybut do każdego istniejącego elementu w „prologowej” strukturze XML.

Tworzenie pliku xml

Plik XML tworzymy przy użyciu predykatu

prolog2xml(File).

Po wpisaniu:

?- prolog2xml('test.xml').

zostanie utworzony plik XML o nazwie test.

Możemy też skorzystać z predykatu

save(File).

który zapisze w podanym pliku aktualną „prologową” strukturę XML oraz aktualny stan nadanych id, np.

save('wiedza.pl').

Przykład dokumentu XML wygenerowanego z użyciem API znajduje się tutaj.

Projekt

Przebieg projektu znajduje się tutaj.

Materiały

Wszelkie materiały dostępne: tutaj.