XML Query Use Cases with xml.pl: Difference between revisions
mNo edit summary |
|||
| (3 intermediate revisions by the same user not shown) | |||
| Line 2: | Line 2: | ||
The following is a complete example to illustrate how the xml.pl module can be used. | The following is a complete example to illustrate how the xml.pl module can be used. | ||
It exercises both the input and output parsing modes of <code>xml_parse/[2,3]</code>, and illustrates | It exercises both the input and output parsing modes of <code>xml_parse/[2,3]</code>, and illustrates | ||
the use of <code>xml_subterm/2</code> to access the nodes of a | the use of <code>xml_subterm/2</code> to access the nodes of a “document value model”. | ||
It's written for Quintus Prolog, but should port to other Prologs easily. | It's written for Quintus Prolog, but should port to other Prologs easily. | ||
====test( +QueryId )==== | ====test( +QueryId )==== | ||
The <code>test/1</code> predicate is the entry-point of the program and | The <code>test/1</code> predicate is the entry-point of the program and | ||
executes a Prolog implementation of a Query from [ | executes a Prolog implementation of a Query from [https://www.w3.org/TR/xquery-use-cases/#xmp Use Case “XMP”: Experiences and Exemplars], in the W3C's XML Query Use Cases, which “contains several example queries that illustrate requirements gathered from the database and document communities”. | ||
<var>QueryId</var> is one of <code>q1</code>...<code>q12</code> selecting which of the 12 use cases is executed. | <var>QueryId</var> is one of <code>q1</code>...<code>q12</code> selecting which of the 12 use cases is executed. | ||
The XML output is written to the file [QueryId].xml in the current directory. | The XML output is written to the file [QueryId].xml in the current directory. | ||
<code>xml_pp/1</code> is used to display the resulting | <code>xml_pp/1</code> is used to display the resulting “document value model” data-structures on the user output (stdout) stream. | ||
< | <pre class="prolog">test( Query ) :- | ||
xml_query( Query, ResultElement ), | xml_query( Query, ResultElement ), | ||
% Parse output XML into the Output chars | % Parse output XML into the Output chars | ||
| Line 24: | Line 24: | ||
% Pretty print OutputXML | % Pretty print OutputXML | ||
write( 'Output XML' ), nl, | write( 'Output XML' ), nl, | ||
xml_pp( xml([], [ResultElement]) ).</ | xml_pp( xml([], [ResultElement]) ).</pre> | ||
====xml_query( +QueryNo, ?OutputXML )==== | ====xml_query( +QueryNo, ?OutputXML )==== | ||
when <var>OutputXML</var> is an XML Document Value Model produced by running an example, identified by <var>QueryNo</var>, taken from the XML Query | when <var>OutputXML</var> is an XML Document Value Model produced by running an example, identified by <var>QueryNo</var>, taken from the XML Query “XMP” use case. | ||
===Q1=== | ===Q1=== | ||
List books published by Addison-Wesley after 1991, including their year and title. | List books published by Addison-Wesley after 1991, including their year and title. | ||
< | <pre class="prolog">xml_query( q1, element(bib, [], Books) ) :- | ||
element_name( Title, title ), | element_name( Title, title ), | ||
element_name( Publisher, publisher ), | element_name( Publisher, publisher ), | ||
| Line 49: | Line 49: | ||
), | ), | ||
Books | Books | ||
).</ | ).</pre> | ||
===Q2=== | ===Q2=== | ||
Create a flat list of all the title-author pairs, with each pair enclosed in a | Create a flat list of all the title-author pairs, with each pair enclosed in a “result” element. | ||
< | <pre class="prolog">xml_query( q2, element(results, [], Results) ) :- | ||
element_name( Title, title ), | element_name( Title, title ), | ||
element_name( Author, author ), | element_name( Author, author ), | ||
| Line 66: | Line 66: | ||
), | ), | ||
Results | Results | ||
).</ | ).</pre> | ||
===Q3=== | ===Q3=== | ||
For each book in the bibliography, list the title and authors, grouped inside a | For each book in the bibliography, list the title and authors, grouped inside a “result” element. | ||
< | <pre class="prolog">xml_query( q3, element(results, [], Results) ) :- | ||
element_name( Title, title ), | element_name( Title, title ), | ||
element_name( Author, author ), | element_name( Author, author ), | ||
| Line 83: | Line 83: | ||
), | ), | ||
Results | Results | ||
).</ | ).</pre> | ||
===Q4=== | ===Q4=== | ||
For each author in the bibliography, list the author's name and the titles of all books by that author, grouped inside a | For each author in the bibliography, list the author's name and the titles of all books by that author, grouped inside a “result” element. | ||
< | <pre class="prolog">xml_query( q4, element(results, [], Results) ) :- | ||
element_name( Title, title ), | element_name( Title, title ), | ||
element_name( Author, author ), | element_name( Author, author ), | ||
| Line 107: | Line 107: | ||
), | ), | ||
Results | Results | ||
).</ | ).</pre> | ||
===Q5=== | ===Q5=== | ||
For each book found at both bn.com and amazon.com, list the title of the book and its price from each source. | For each book found at both bn.com and amazon.com, list the title of the book and its price from each source. | ||
< | <pre class="prolog">xml_query( q5, element('books-with-prices', [], BooksWithPrices) ) :- | ||
element_name( Title, title ), | element_name( Title, title ), | ||
element_name( Book, book ), | element_name( Book, book ), | ||
| Line 132: | Line 132: | ||
), | ), | ||
BooksWithPrices | BooksWithPrices | ||
).</ | ).</pre> | ||
===Q6=== | ===Q6=== | ||
For each book that has at least one author, list the title and first two authors, and an empty | For each book that has at least one author, list the title and first two authors, and an empty “et-al” element if the book has additional authors. | ||
< | <pre class="prolog">xml_query( q6, element(bib, [], Results) ) :- | ||
element_name( Title, title ), | element_name( Title, title ), | ||
element_name( Author, author ), | element_name( Author, author ), | ||
| Line 150: | Line 150: | ||
), | ), | ||
Results | Results | ||
).</ | ).</pre> | ||
===Q7=== | ===Q7=== | ||
List the titles and years of all books published by Addison-Wesley after 1991, in alphabetic order. | List the titles and years of all books published by Addison-Wesley after 1991, in alphabetic order. | ||
< | <pre class="prolog">xml_query( q7, element(bib, [], Books) ) :- | ||
element_name( Title, title ), | element_name( Title, title ), | ||
element_name( Publisher, publisher ), | element_name( Publisher, publisher ), | ||
| Line 173: | Line 173: | ||
), | ), | ||
keysort( TitleBooks, TitleBookSet ), | keysort( TitleBooks, TitleBookSet ), | ||
range( TitleBookSet, Books ).</ | range( TitleBookSet, Books ).</pre> | ||
===Q8=== | ===Q8=== | ||
Find books in which the name of some element ends with the string | Find books in which the name of some element ends with the string “or” and the same element contains the string “Suciu” somewhere in its content. For each such book, return the title and the qualifying element. | ||
< | <pre class="prolog">xml_query( q8, element(bib, [], Books) ) :- | ||
element_name( Title, title ), | element_name( Title, title ), | ||
element_name( Book, book ), | element_name( Book, book ), | ||
| Line 196: | Line 196: | ||
), | ), | ||
Books | Books | ||
).</ | ).</pre> | ||
===Q9=== | ===Q9=== | ||
In the document | In the document “books.xml”, find all section or chapter titles that contain the word “XML”, regardless of the level of nesting. | ||
< | <pre class="prolog">xml_query( q9, element(results, [], Titles) ) :- | ||
element_name( Title, title ), | element_name( Title, title ), | ||
append( "XML", _Back, Suffix ), | append( "XML", _Back, Suffix ), | ||
| Line 213: | Line 213: | ||
), | ), | ||
Titles | Titles | ||
).</ | ).</pre> | ||
===Q10=== | ===Q10=== | ||
In the document | 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. | ||
< | <pre class="prolog">xml_query( q10, element(results, [], MinPrices) ) :- | ||
element_name( Title, title ), | element_name( Title, title ), | ||
element_name( Price, price ), | element_name( Price, price ), | ||
| Line 243: | Line 243: | ||
), | ), | ||
MinPrices | MinPrices | ||
).</ | ).</pre> | ||
===Q11=== | ===Q11=== | ||
For each book with an author, return the book with its title and authors. For each book with an editor, return a reference with the book title and the editor's affiliation. | For each book with an author, return the book with its title and authors. For each book with an editor, return a reference with the book title and the editor's affiliation. | ||
< | <pre class="prolog">xml_query( q11, element(bib, [], Results) ) :- | ||
element_name( Title, title ), | element_name( Title, title ), | ||
element_name( Author, author ), | element_name( Author, author ), | ||
| Line 273: | Line 273: | ||
References | References | ||
), | ), | ||
append( Books, References, Results ).</ | append( Books, References, Results ).</pre> | ||
===Q12=== | ===Q12=== | ||
Find pairs of books that have different titles but the same set of authors (possibly in a different order). | Find pairs of books that have different titles but the same set of authors (possibly in a different order). | ||
< | <pre class="prolog">xml_query( q12, element(bib, [], Pairs) ) :- | ||
element_name( Author, author ), | element_name( Author, author ), | ||
element_name( Book1, book ), | element_name( Book1, book ), | ||
| Line 298: | Line 298: | ||
), | ), | ||
Pairs | Pairs | ||
).</ | ).</pre> | ||
== Auxiliary Predicates == | == Auxiliary Predicates == | ||
< | <pre class="prolog">other_authors( [], [] ). | ||
other_authors( [Author|Authors], [Author|EtAl] ) :- | other_authors( [Author|Authors], [Author|EtAl] ) :- | ||
et_al( Authors, EtAl ). | et_al( Authors, EtAl ). | ||
| Line 311: | Line 311: | ||
text_value( [cdata(Text)], Text ). | text_value( [cdata(Text)], Text ). | ||
element_name( element(Name, _Attributes, _Content), Name ).</ | element_name( element(Name, _Attributes, _Content), Name ).</pre> | ||
====range( +Pairs, ?Range )==== | ====range( +Pairs, ?Range )==== | ||
when <var>Pairs</var> is a list of key-datum pairs and <var>Range</var> is the list of data. | when <var>Pairs</var> is a list of key-datum pairs and <var>Range</var> is the list of data. | ||
< | <pre class="prolog">range( [], [] ). | ||
range( [_Key-Datum|Pairs], [Datum|Data] ) :- | range( [_Key-Datum|Pairs], [Datum|Data] ) :- | ||
range( Pairs, Data ).</ | range( Pairs, Data ).</pre> | ||
====minimum( +List, ?Min )==== | ====minimum( +List, ?Min )==== | ||
is true if <var>Min</var> is the least member of <var>List</var> in the standard order. | is true if <var>Min</var> is the least member of <var>List</var> in the standard order. | ||
< | <pre class="prolog">minimum( [H|T], Min ):- | ||
minimum1( T, H, Min ). | minimum1( T, H, Min ). | ||
| Line 332: | Line 332: | ||
minimum1( T, Min0, Min ). | minimum1( T, Min0, Min ). | ||
minimum2( '>', _Min0, Min1, T, Min ) :- | minimum2( '>', _Min0, Min1, T, Min ) :- | ||
minimum1( T, Min1, Min ).</ | minimum1( T, Min1, Min ).</pre> | ||
====input_document( +File, ?XML )==== | ====input_document( +File, ?XML )==== | ||
reads <var>File</var> and parses the input into the | reads <var>File</var> and parses the input into the “Document Value Model” <var>XML</var>. | ||
< | <pre class="prolog">input_document( File, XML ) :- | ||
% Read InputFile as a list of chars | % Read InputFile as a list of chars | ||
see( File ), | see( File ), | ||
| Line 341: | Line 341: | ||
seen, | seen, | ||
% Parse the Input chars into the term XML | % Parse the Input chars into the term XML | ||
xml_parse( Input, XML ).</ | xml_parse( Input, XML ).</pre> | ||
Load the [[XML Module]]. | Load the [[XML Module]]. | ||
< | <pre class="prolog">:- use_module( xml ).</pre> | ||
Load a small library of [[Puzzle Utilities]]. | Load a small library of [[Puzzle Utilities]]. | ||
< | <pre class="prolog"> | ||
:- ensure_loaded( misc ). | :- ensure_loaded( misc ). | ||
</ | </pre> | ||
Download a 5Kb tar.gz format [https://binding-time.co.uk/download/xml_example.tar.gz file containing this program with input and output data]. | Download a 5Kb tar.gz format [https://binding-time.co.uk/download/xml_example.tar.gz file containing this program with input and output data]. | ||
Latest revision as of 19:11, 1 August 2022
The following is a complete example to illustrate how the xml.pl module can be used.
It exercises both the input and output parsing modes of xml_parse/[2,3], and illustrates
the use of xml_subterm/2 to access the nodes of a “document value model”.
It's written for Quintus Prolog, but should port to other Prologs easily.
test( +QueryId )
The test/1 predicate is the entry-point of the program and
executes a Prolog implementation of a Query from Use Case “XMP”: Experiences and Exemplars, in the W3C's XML Query Use Cases, which “contains several example queries that illustrate requirements gathered from the database and document communities”.
QueryId is one of q1...q12 selecting which of the 12 use cases is executed.
The XML output is written to the file [QueryId].xml in the current directory.
xml_pp/1 is used to display the resulting “document value model” data-structures on the user output (stdout) stream.
test( Query ) :-
xml_query( Query, ResultElement ),
% Parse output XML into the Output chars
xml_parse( Output, xml([], [ResultElement]) ),
absolute_file_name( Query, [extensions(xml)], OutputFile ),
% Write OutputFile from the Output list of chars
tell( OutputFile ),
put_chars( Output ),
told,
% Pretty print OutputXML
write( 'Output XML' ), nl,
xml_pp( xml([], [ResultElement]) ).
xml_query( +QueryNo, ?OutputXML )
when OutputXML is an XML Document Value Model produced by running an example, identified by QueryNo, taken from the XML Query “XMP” use case.
Q1
List books published by Addison-Wesley after 1991, including their year and title.
xml_query( q1, element(bib, [], Books) ) :-
element_name( Title, title ),
element_name( Publisher, publisher ),
input_document( 'bib.xml', Bibliography ),
findall(
element(book, [year=Year], [Title]),
(
xml_subterm( Bibliography, element(book, Attributes, Content) ),
xml_subterm( Content, Publisher ),
xml_subterm( Publisher, Text ),
text_value( Text, "Addison-Wesley" ),
member( year=Year, Attributes ),
number_codes( YearNo, Year ),
YearNo > 1991,
xml_subterm( Content, Title )
),
Books
).
Q2
Create a flat list of all the title-author pairs, with each pair enclosed in a “result” element.
xml_query( q2, element(results, [], Results) ) :-
element_name( Title, title ),
element_name( Author, author ),
element_name( Book, book ),
input_document( 'bib.xml', Bibliography ),
findall(
element(result, [], [Title,Author]),
(
xml_subterm( Bibliography, Book ),
xml_subterm( Book, Title ),
xml_subterm( Book, Author )
),
Results
).
Q3
For each book in the bibliography, list the title and authors, grouped inside a “result” element.
xml_query( q3, element(results, [], Results) ) :-
element_name( Title, title ),
element_name( Author, author ),
element_name( Book, book ),
input_document( 'bib.xml', Bibliography ),
findall(
element(result, [], [Title|Authors]),
(
xml_subterm( Bibliography, Book ),
xml_subterm( Book, Title ),
findall( Author, xml_subterm(Book, Author), Authors )
),
Results
).
Q4
For each author in the bibliography, list the author's name and the titles of all books by that author, grouped inside a “result” element.
xml_query( q4, element(results, [], Results) ) :-
element_name( Title, title ),
element_name( Author, author ),
element_name( Book, book ),
input_document( 'bib.xml', Bibliography ),
findall( Author, xml_subterm(Bibliography, Author), AuthorBag ),
sort( AuthorBag, Authors ),
findall(
element(result, [], [Author|Titles]),
(
member( Author, Authors ),
findall( Title, (
xml_subterm( Bibliography, Book ),
xml_subterm( Book, Author ),
xml_subterm( Book, Title )
),
Titles
)
),
Results
).
Q5
For each book found at both bn.com and amazon.com, list the title of the book and its price from each source.
xml_query( q5, element('books-with-prices', [], BooksWithPrices) ) :-
element_name( Title, title ),
element_name( Book, book ),
element_name( Review, entry ),
input_document( 'bib.xml', Bibliography ),
input_document( 'reviews.xml', Reviews ),
findall(
element('book-with-prices', [], [
Title,
element('price-bn',[], BNPrice ),
element('price-amazon',[], AmazonPrice )
] ),
(
xml_subterm( Bibliography, Book ),
xml_subterm( Book, Title ),
xml_subterm( Reviews, Review ),
xml_subterm( Review, Title ),
xml_subterm( Book, element(price,_, BNPrice) ),
xml_subterm( Review, element(price,_, AmazonPrice) )
),
BooksWithPrices
).
Q6
For each book that has at least one author, list the title and first two authors, and an empty “et-al” element if the book has additional authors.
xml_query( q6, element(bib, [], Results) ) :-
element_name( Title, title ),
element_name( Author, author ),
element_name( Book, book ),
input_document( 'bib.xml', Bibliography ),
findall(
element(book, [], [Title,FirstAuthor|Authors]),
(
xml_subterm( Bibliography, Book ),
xml_subterm( Book, Title ),
findall( Author, xml_subterm(Book, Author), [FirstAuthor|Others] ),
other_authors( Others, Authors )
),
Results
).
Q7
List the titles and years of all books published by Addison-Wesley after 1991, in alphabetic order.
xml_query( q7, element(bib, [], Books) ) :-
element_name( Title, title ),
element_name( Publisher, publisher ),
input_document( 'bib.xml', Bibliography ),
findall(
Title-element(book, [year=Year], [Title]),
(
xml_subterm( Bibliography, element(book, Attributes, Book) ),
xml_subterm( Book, Publisher ),
xml_subterm( Publisher, Text ),
text_value( Text, "Addison-Wesley" ),
member( year=Year, Attributes ),
number_codes( YearNo, Year ),
YearNo > 1991,
xml_subterm( Book, Title )
),
TitleBooks
),
keysort( TitleBooks, TitleBookSet ),
range( TitleBookSet, Books ).
Q8
Find books in which the name of some element ends with the string “or” and the same element contains the string “Suciu” somewhere in its content. For each such book, return the title and the qualifying element.
xml_query( q8, element(bib, [], Books) ) :-
element_name( Title, title ),
element_name( Book, book ),
element_name( QualifyingElement, QualifyingName ),
append( "Suciu", _Back, Suffix ),
input_document( 'bib.xml', Bibliography ),
findall(
element(book, [], [Title,QualifyingElement]),
(
xml_subterm( Bibliography, Book ),
xml_subterm( Book, QualifyingElement ),
atom_codes( QualifyingName, QNChars ),
append( _QNPrefix, "or", QNChars ),
xml_subterm( QualifyingElement, TextItem ),
text_value( TextItem, TextValue ),
append( _Prefix, Suffix, TextValue ),
xml_subterm( Book, Title )
),
Books
).
Q9
In the document “books.xml”, find all section or chapter titles that contain the word “XML”, regardless of the level of nesting.
xml_query( q9, element(results, [], Titles) ) :-
element_name( Title, title ),
append( "XML", _Back, Suffix ),
input_document( 'books.xml', Books ),
findall(
Title,
(
xml_subterm( Books, Title ),
xml_subterm( Title, TextItem ),
text_value( TextItem, TextValue ),
append( _Prefix, Suffix, TextValue )
),
Titles
).
Q10
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.
xml_query( q10, element(results, [], MinPrices) ) :-
element_name( Title, title ),
element_name( Price, price ),
input_document( 'prices.xml', Prices ),
findall( Title, xml_subterm(Prices, Title), TitleBag ),
sort( TitleBag, TitleSet ),
element_name( Book, book ),
findall(
element(minprice, [title=TitleString], [MinPrice]),
(
member( Title, TitleSet ),
xml_subterm( Title, TitleText ),
text_value( TitleText, TitleString ),
findall( PriceValue-Price, (
xml_subterm( Prices, Book ),
xml_subterm( Book, Title ),
xml_subterm( Book, Price ),
xml_subterm( Price, Text ),
text_value( Text, PriceChars ),
number_codes( PriceValue, PriceChars )
),
PriceValues
),
minimum( PriceValues, PriceValue-MinPrice )
),
MinPrices
).
Q11
For each book with an author, return the book with its title and authors. For each book with an editor, return a reference with the book title and the editor's affiliation.
xml_query( q11, element(bib, [], Results) ) :-
element_name( Title, title ),
element_name( Author, author ),
element_name( Book, book ),
element_name( Editor, editor ),
element_name( Affiliation, affiliation ),
input_document( 'bib.xml', Bibliography ),
findall(
element(book, [], [Title,FirstAuthor|Authors]),
(
xml_subterm( Bibliography, Book ),
xml_subterm( Book, Title ),
findall( Author, xml_subterm(Book, Author), [FirstAuthor|Authors] )
),
Books
),
findall(
element(reference, [], [Title,Affiliation]),
(
xml_subterm( Bibliography, Book ),
xml_subterm( Book, Title ),
xml_subterm( Book, Editor ),
xml_subterm( Editor, Affiliation )
),
References
),
append( Books, References, Results ).
Q12
Find pairs of books that have different titles but the same set of authors (possibly in a different order).
xml_query( q12, element(bib, [], Pairs) ) :-
element_name( Author, author ),
element_name( Book1, book ),
element_name( Book2, book ),
element_name( Title1, title ),
element_name( Title2, title ),
input_document( 'bib.xml', Bibliography ),
findall(
element('book-pair', [], [Title1,Title2]),
(
xml_subterm( Bibliography, Book1 ),
findall( Author, xml_subterm(Book1, Author), AuthorBag1 ),
sort( AuthorBag1, AuthorSet ),
xml_subterm( Bibliography, Book2 ),
Book2 @< Book1,
findall( Author, xml_subterm(Book2, Author), AuthorBag2 ),
sort( AuthorBag2, AuthorSet ),
xml_subterm( Book1, Title1 ),
xml_subterm( Book2, Title2 )
),
Pairs
).
Auxiliary Predicates
other_authors( [], [] ).
other_authors( [Author|Authors], [Author|EtAl] ) :-
et_al( Authors, EtAl ).
et_al( [], [] ).
et_al( [_|_], [element('et-al',[],[])] ).
text_value( [pcdata(Text)], Text ).
text_value( [cdata(Text)], Text ).
element_name( element(Name, _Attributes, _Content), Name ).
range( +Pairs, ?Range )
when Pairs is a list of key-datum pairs and Range is the list of data.
range( [], [] ).
range( [_Key-Datum|Pairs], [Datum|Data] ) :-
range( Pairs, Data ).
minimum( +List, ?Min )
is true if Min is the least member of List in the standard order.
minimum( [H|T], Min ):-
minimum1( T, H, Min ).
minimum1( [], Min, Min ).
minimum1( [H|T], Min0, Min ) :-
compare( Relation, H, Min0 ),
minimum2( Relation, H, Min0, T, Min ).
minimum2( '=', Min0, Min0, T, Min ) :-
minimum1( T, Min0, Min ).
minimum2( '<', Min0, _Min1, T, Min ) :-
minimum1( T, Min0, Min ).
minimum2( '>', _Min0, Min1, T, Min ) :-
minimum1( T, Min1, Min ).
input_document( +File, ?XML )
reads File and parses the input into the “Document Value Model” XML.
input_document( File, XML ) :-
% Read InputFile as a list of chars
see( File ),
get_chars( Input ),
seen,
% Parse the Input chars into the term XML
xml_parse( Input, XML ).
Load the XML Module.
:- use_module( xml ).
Load a small library of Puzzle Utilities.
:- ensure_loaded( misc ).
Download a 5Kb tar.gz format file containing this program with input and output data.