Examples

Here we present several examples of XCentric programs. Some of these examples are from the XQuery use cases proposed by W3C. Also note that the distribution of XCentric includes these and several other examples.

Example 1. Given this XML file with a set of publications, detect which have a publisher tag and which don't and for those having a publisher tag, print its content:

verpub(URL):-
      http2pro(URL,Pubs),
      Pubs =*= pubs([],_,pub([],_,title([],NP),Rem),_),
      write(NP),write(' '),decide(Rem),nl.

decide(<_,publisher([],Pub),_>):-
      write('has publisher: '),write(Pub),!.

decide(_):-
     write('does not have publisher.').

We start by extracting the sequence of elements possible including the publisher tag and output a list of publications indicating the publisher name when it is available. The result follows:

Type-based XML Processing in Logic Programming has publisher: Springer Verlag

Linearization of the Lambda-Calculus and its Relation with Intersection Type Systems does not have publisher.

On Avoiding Redundancy in Inductive Logic Programming has publisher: Springer Verlag

Mob: A Scripting Language for Mobile Agents based on a Process Calculus has publisher: Springer Verlag

Example 2. In this example we have one Xml document for an address book (addressbook.xml). This document has records with optional email tags. Here we want to create a new document containing only the records with one or more email tags. We define the type of the record and process the document has described (note that the <X1,...Xn> unifies with a sequence of n elements):

:-use_module([xcentric,xml2prolog,prolog2xml]).

:- type tr ---> record([],name([],string),address([],string),phone([],string)?,email([],string)+).

translate:-
     xml2pro('addressbook.xml',Xml),
     process(Xml,NewXml),
     pro2xml(NewXml,'addressbook2.xml').

process(A,NewA):-
     A =*= addressbook([],A2),
     records_with_email(A2,A3),
     newdoc(addressbook,[],A3,NewA).

records_with_email(<_,X::tr,S2>,<X,S3>):-!,
     records_with_email(S2,S3).

records_with_email(_,<>).

In this example types act like filters for the data.

Example 3. Given document bibs.xml containing a list of books, create a new document books published by Addison-Wesley after 1991, including their year and title:

translate:-
    xml2pro('./examples-xquery/bibs.xml',Xml),
    process(Xml,NewXml),
    pro2xml(NewXml,'./examples-xquery/bib2.xml').

process(A,NewA):-
    bib([],A2) =~= A,
    query_pub(A2,A3),
    newdoc(bib,[],A3,NewA).

query_pub(<_,book([attribute(year,Y)],title([],T),_,publisher([],'Addison-Wesley'),_),S1>,
          <book([attribute(year,Y)],title([],T)),S2>):-
    var(S2),
    atom_number(Y,Yn),Yn>1991,!,
    query_pub(S1,S2).

query_pub(_,<>).

The result is:

<bib>
    <book year=1994>
        <title>TCP/IP Illustrated</title>
    </book>
    <book year=1992>
        <title>Advanced Programming in the Unix environment</title>
    </book>
</bib>

Example 4. Given the files bibs.xml and reviews.xml, both containing entries of books, create a new file with the prices on both files for each book:

translate:-
    xml2pro('./examples-xquery/bibs.xml',Bib),
    xml2pro('./examples-xquery/reviews.xml',Rev),
    process(Bib,Rev,NewXml),
    pro2xml(NewXml,'./examples-xquery/prices.xml').

process(<bib([],B)>,<reviews([],R)>,NewA):-
    findall(F,query_pub(B,R,F),FL),
    newdoc(books_with_prices,[],FL,NewA).

query_pub(B,R,book_with_price([],title([],T),price_bstore2([],P2),price_bstore1([],P1))):-
    <_,book(_,title([],T),_,price([],P1)),S1> =~= B,
    <_,entry([],title([],T),price([],P2),_),S2> =~= R.

The result is:
<books-with-prices>
<book-with-prices>
<title>TCP/IP Illustrated</title>
<price-bstore2>65.95</price-bstore2>
<price-bstore1>65.95</price-bstore1>
</book-with-prices>
<book-with-prices>
<title>Advanced Programming in the Unix environment</title>
<price-bstore2>65.95</price-bstore2>
<price-bstore1>65.95</price-bstore1>
</book-with-prices>
<book-with-prices>
<title>Data on the Web</title>
<price-bstore2>34.95</price-bstore2>
<price-bstore1>39.95</price-bstore1>
</book-with-prices>
</books-with-prices>
Example 5. In the document prices.xml, find the minimum price for each book, in the form of a "minprice" element with the book title as its title attribute:

translate:-
    xml2pro('./examples-xquery/prices.xml',Pri),
    setof(X,min_price(Pri,X),NewXml),
    newdoc(results,[],NewXml,F),
    pro2xml(F,'./examples-xquery/min-prices.xml').


min_price(<prices([],Books)>,minprice([attribute(title,T)],price([],M))):-
    <_,book([],title([],T),_),_> =~= Books,
    findall(Price,book_price(T,Books,Price),PL),
    min(PL,M).

book_price(T,<_,book([],title([],T),_,price([],P)),_>,P).

The result is:

<results>
<minprice title="Advanced Programming in the Unix environment">
<price>65.95</price>
</minprice>
<minprice title="TCP/IP Illustrated">
<price>65.95</price>
</minprice>
<minprice title="Data on the Web">
<price>34.95</price>
</minprice>
</results>

Example 6. This use case is based on a medical report using the HL7 Patient Record Architecture. Given this report1.xml, find what happened between the first Incision and the second Incision and write the result in a file named critical.xml:

translate:-
    xml2pro('./examples-xquery/report1.xml',Rep),
    deep(<incision(_),Critical,incision(_)>,Rep),
    newdoc(critical_sequence,[],Critical,FL),
    pro2xml(FL,'./examples-xquery/critical.xml').

The reult is:

<critical_sequence>
      The fascia was identified and<action> #2 0 Maxon stay sutures were placed on each side of the midline.</action>
</critical_sequence>

Example 7. Given document book.xml, prepare a (flat) figure list for it, creating new file figlist.xml where all the figures and their titles. Preserve the original attributes of each <figure> element, if any.

translate:-
    xml2pro('./examples-xquery/book.xml',Book),
    findall(figure(A,title([],T)),deep(figure(A,title([],T),_),Book),Fig),
    newdoc(figlist,[],Fig,FL),
    pro2xml(FL,'./examples-xquery/figlist.xml').

The result is:

<figlist>
        <figure height=400 width=400>
                <title>
                        Traditional client/server architecture
                </title>
        </figure>
        <figure height=200 width=500>
                <title>
                        Graph representations of structures
                </title>
        </figure>
        <figure height=250 width=400>
                <title>
                        Examples of Relations
                </title>
        </figure>
</figlist>

Example 8. Given file string.xml with a list of news, find the titles of all news items where the string "Foobar Corporation" appears in the title.

translate:-
    xml2pro('./examples-xquery/string.xml',XML),
    findall(T,foobar(XML,T),LT).

foobar(XML,title([],T)):-
    deep(news_item([],title([],T),_),XML),
    substring('Foobar Corporation',T),
    write('<title>'),write(T),write('</title>'),nl.

The result is:

<title>Foobar Corporation releases its new line of Foo
products today</title>
<title>Foobar Corporation is suing Gorilla Corporation for patent
infringement </title>

Example 9. Given the document candidates.xml, containing a list of candidates to
a job, select the candidates with two or more previous jobs in their curriculum:

:- type jobs ---> previous_job([],name([],string),address([],string)){2,unbounded}.

select(N):-
    xml2pro('candidates.xml',Can),
    doc([],_,person([],name([],N),X::jobs),_) =~= Can. 

The result is:

N = 'David Pereira' 

Example 10. In this example we have two XML files, a simple text.xml file conforming to text.dtd and a bib.xml file with simple references conforming to bib.dtd. Our text.xml <ref> elements contain author names which appear in the <author> element of bib.xml. Our goal is to process text.xml and bib.xml and generate new text2.xml and bib2.xml files. The text2.xml file is almost the same than text.xml but <ref> elements are replaced by new <i> elements where the content is replaced by a number. This number represents the index of that reference ordered by author within the document. The bib2.xml contains only the references appearing in text.xml, ordered by author, and with a label element with the corresponding number that occurs in the text2.xml. The program follows:

:-use_module([xcentric,xml2prolog,prolog2xml]).

:-type t2 ---> string; ref([],string); b([],string).
:-type t ---> t2*.
:-type i2 ---> string; i([],string); b([],string).
:-type i ---> i2*.
:-type str ---> string.
:-type bib ---> bibliography([],bib([],author([],string),name([],string))*).

run:-
    xml2pro('text.xml',T),
    process(T).

process(T):-
    T =*= text([],CT),
    process2(CT,T2,[],Refs),
    xml2pro('bib.xml',B),
    add_bib(Refs,B,BibXML,1),
    newdoc(bibliography,[],BibXML,NewBIB),
    newdoc(text,[],T2,NewText),
    pro2xml(NewText,'text2.xml'),
    pro2xml(NewBIB,'bib2.xml').

process2(<>,<>,L,L).

process2(A,B,L,RRefs):-
    A::t =*= <X,ref([],R),Y>,
    B::i =*= <X,i([],NewVar::str),Y2>,
    insert_sorted(R,NewVar,L,Refs),
    process2(Y,Y2,Refs,RRefs).

process2(S,S,L,L).

add_bib([],_,[],_).

add_bib([(A,AI)|Refs],B,[label([],AI),author([],A),name([],N)|XML],I):-
    B::bib =*= bibliography([],_,bib([],author([],A),name([],N)),_),
    I1 is I + 1,
    atom_chars(I,AI),
    add_bib(Refs,B,XML,I1).

insert_sorted(X,Var,[],[(X,Var)]).

insert_sorted(X,Var,[(Y,Z)|R],[(X,Var),(Y,Z)|R]):-
    strcmp(X,Y,I),
    I < 0,!.

insert_sorted(X,Var,[(Y,Z)|R1],[(Y,Z)|R2]):-
    insert_sorted(X,Var,R1,R2).

strcmp(S1,S2,I):-
    string_to_list(S1,L1),
    string_to_list(S2,L2),
    lcomp(L1,L2,I).

lcomp([],[],0).

lcomp([X|R1],[X|R2],N):-!,
    lcomp(R1,R2,N).

lcomp([X|_],[Y|_],N):-
    N is X - Y.

 

The run predicate starts the translation process. In a first step, the process2 predicate translates the original text into a new one where the <ref> elements are replaced by new <i> elements and the content is replaced by a new free variable. At the same time pairs (Ref_content,FreeVariable) (corresponding to the author in <ref> and the new variable in <i> respectively) are inserted in a list ordered by Ref_content. At the end we have a list of pairs ordered by author. In a second step, add_bib queries the bibliography file, retrieving the references found in the text and replacing the free variables with their definite content, thus, avoiding a second processing of the text file.
This example emphasizes some advantages of logic programming languages for XML processing; it shows an highly declarative and compact code and the advantages of using logic variables that permit to solve the problem by processing the text.xml document only once. Types guarantee correctness of values given as input and the results of processing.