Rev 8141 | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed
\name{xmlEventParse}\alias{xmlEventParse}\title{ XML Event/Callback element-wise Parser}\description{This is the event-driven or SAX (Simple API for XML)style parser which process XML without building the treebut rather identifies tokens in the stream of charactersand passes them to handlers which can make sense of themin context.This reads and processes the contents of an XML file or string byinvoking user-level functions associated with differentcomponents of the XML tree. These components includethe beginning and end of XML elements, e.g\code{<myTag x="1">}and \code{</myTag>} respectively,comments, CDATA (escaped character data), entities, processinginstructions, etc.This allows the caller to create the appropriate data structure from theXML document contents rather than the default tree (see\link{xmlTreeParse})and so avoids having the entire document in memory.This is important for large documents and where we would end up withessentially 2 copies of the data in memory at once, i.ethe tree and the R data structure containing the information takenfrom the tree.When dealing with classes of XML documents whose instances could be large,this approach is desirable but a little more cumbersome to programthan the standard DOM (Document Object Model) approach providedby \code{XMLTreeParse}.Note that \code{xmlTreeParse} does allow a hybrid style ofprocessing that allows us to apply handlers to nodes in the treeas they are being converted to R objects. This is a style ofevent-driven or asynchronous callingIn addition to the generic token event handlers such as"begin an XML element" (the \code{startElement} handler), one canalso provide handler functions for specific tags/elements suchas \code{<myTag>} with handler elements with the same name as theXML element of interest, i.e. \code{"myTag" = function(x, attrs)}.When the event parser is reading text nodes,it may call the text handler function with differentsub-strings of the text within the node.Essentially, the parser collects up n characters into a buffer andpasses this as a single string the text handler and then continuescollecting more text until the buffer is full or there is no more text.It passes each sub-string to the text handler.If \code{trim} is \code{TRUE}, it removes leading and trailing whitespace from the substring before calling the text handler. If theresulting text is empty and \code{ignoreBlanks} is \code{TRUE},then we don't bother calling the text handler function.So the key thing to remember about dealing with text is that theentire text of a node may come in multiple separate callsto the text handler. A common idiom is to have the text handlerconcatenate the values it is passed in separate calls andto have the end element handler process the entire text and resetthe text variable to be empty.}\usage{xmlEventParse(file, handlers = xmlEventHandler(),ignoreBlanks = FALSE, addContext=TRUE,useTagName = TRUE, asText = FALSE, trim=TRUE,useExpat=FALSE, isURL = FALSE,state = NULL, replaceEntities = TRUE, validate = FALSE,saxVersion = 1, branches = NULL,useDotNames = length(grep("^\\\\.", names(handlers))) > 0,error = xmlErrorCumulator(), addFinalizer = NA,encoding = character())}\arguments{\item{file}{the source of the XML content.This can be a string giving the name of a file or remote URL,the XML itself, a connection object, or a function.If this is a string, and \code{asText} is \code{TRUE},the value is the XML content.This allows one to read the content separately from parsingwithout having to write it to a file.If \code{asText} is \code{FALSE} and a string is passedfor \code{file}, this is taken as the name of afile or remote URI. If one is using the libxml parser (i.e. not expat),this can be a URI accessed via HTTP or FTP or a compressed local file.If it is the name of a local file,it can include \code{~}, environment variables, etc. which will be expanded by R.(Note this is not the case in S-Plus, as far as I know.)If a connection is given, the parser incrementally reads one line ata time by calling the function \code{\link{readLines}} withthe connection as the first argument (and \code{1} as the number oflines to read). The parser calls this function each time it needsmore input.If invoking the \code{readLines} function to get each line isexcessively slow or is inappropriate, one can provide a function as the valueof \code{fileName}. Again, when the XML parser needs more contentto process, it invokes this function to get a string.This function is called with a single argument, the maximum sizeof the string that can be returned.The function is responsible for accessing the correct connection(s),etc. which is typically done via lexical scoping/environments.This mechanism allows the user to control how the XML contentis retrieved in very general ways. For example, one mightread from a set of files, starting one when the contentsof the previous file have been consumed. This allows for theuse of hybrid connection objects.Support for connections and functions in this form is onlyprovided if one is using libxml2 and not libxml version 1.}\item{handlers}{ a closure object that contains functions which will be invokedas the XML components in the document are encountered by the parser.The standard function or handler names are\code{startElement()}, \code{endElement()}\code{comment()}, \code{getEntity},\code{entityDeclaration()}, \code{processingInstruction()},\code{text()}, \code{cdata()},\code{startDocument()}, and \code{endDocument()},or alternatively and preferrably,these names prefixed with a '.',i.e. .startElement, .comment, ...The call signature for the entityDeclaration function was changed inversion 1.7-0. Note that in earlier versions, the C routine did notinvoke any R function and so no code will actually break.Also, we have renamed \code{externalEntity} to \code{getEntity}.These were based on the expat parser.The new signature is\code{c(name = "character",type = "integer",content = "",system = "character",public = "character")}\code{name} gives the name of the entity beingdefined.The \code{type} identifiesthe type of the entity using the valueof a C-level enumerated constant used in libxml2,but also gives the human-readable formas the name of the single element in the integer vector.The possible values are\code{"Internal_General"},\code{"External_General_Parsed"},\code{"External_General_Unparsed"}, \code{"Internal_Parameter"},\code{"External_Parameter"}, \code{"Internal_Predefined"}.If we are dealing with an internal entity,the content will be the string containingthe value of the entity.If we are dealing with an external entity,then \code{content} will be a character vector of length0, i.e. empty.Instead, either or both of the system and publicarguments will be non-empty and identify thelocation of the external content.\code{system} will be a string containing a URI, if non-empty,and \code{public} corresponds to the PUBLIC identifier usedto identify content using an SGML-like approach.The use of PUBLIC identifiers is less common.}\item{ignoreBlanks}{a logical value indicating whethertext elements made up entirely of white space should be includedin the resulting `tree'. }\item{addContext}{ logical value indicating whether the callback functionsin `handlers' should be invoked with contextual information aboutthe parser and the position in the tree, such as node depth,path indices for the node relative the root, etc.If this is True, each callback function should support\dots.}\item{useTagName}{ a logical value.If this is \code{TRUE}, when the SAX parser signals an event for thestart of an XML element, it will first look for an element in thelist of handler functions whose name matches (exactly) the name ofthe XML element. If such an element is found, that function isinvoked. Otherwise, the generic \code{startElement} handler functionis invoked. The benefit of this is that the author of the handlerfunctions can write node-specific handlers for the different elementnames in a document and not have to establish a mechanism to invokethese functions within the \code{startElement} function. This is doneby the XML package directly.If the value is \code{FALSE}, then the \code{startElement} handlerfunction will be called without any effort to find a node-specifichandler. If there are no node-specific handlers, specifying\code{FALSE} for this parameter will make the computations veryslightly faster.}\item{asText}{logical value indicating that the first argument,`file',should be treated as the XML text to parse, not the name ofa file. This allows the contents of documents to be retrievedfrom different sources (e.g. HTTP servers, XML-RPC, etc.) and stilluse this parser.}\item{trim}{whether to strip white space from the beginning and end of text strings.}% \item{restartCounter}{}\item{useExpat}{a logical value indicating whether to use the expat SAX parser,or to default to the libxml.If this is TRUE, the library must have been compiled with support for expat.See \link{supportsExpat}.}\item{isURL}{indicates whether the \code{file} argument refers to a URL(accessible via ftp or http) or a regular file on the system.If \code{asText} is TRUE, this should not be specified.}\item{state}{an optional S object that is passed to thecallbacks and can be modified to communicate state betweenthe callbacks. If this is given, the callbacks should acceptan argument named \code{.state} and it should return an objectthat will be used as the updated value of this state object.The new value can be any S object and will be passed to the nextcallback where again it will be updated by that functions returnvalue, and so on.If this not specified in the call to \code{xmlEventParse},no \code{.state} argument is passed to the callbacks. This makes theinterface compatible with previous releases.}\item{replaceEntities}{logical value indicating whether to substitute entity referenceswith their text directly. This should be left as False.The text still appears as the value of the node, but thereis more information about its source, allowing the parse to be reversedwith full reference information.}\item{saxVersion}{an integer value which should be either 1 or 2.This specifies which SAX interface to use in the C code.The essential difference is the number of arguments passed to the\code{startElement} handler function(s). Under SAX 2, in addition to the name ofthe element and the named-attributes vector, two additional argumentsare provided.The first identifies the namespace of the element.This is a named character vector of length 1,with the value being the URI of the namespace and the namebeing the prefix that identifies that namespace within the document.For example, \code{xmlns:r="http://www.r-project.org"}would be passed as \code{c(r = "http://www.r-project.org")}.If there is no prefix because the namespace is being used as thedefault, the result of calling \code{\link{names}} onthe string is \code{""}.The second additional argument (the fourth in total) gives the collection of all the namespacesdefined within this element.Again, this is a named character vector.}\item{validate}{Currently, this has no effect as the libxml2 parser uses adocument structure to do validation.a logical indicating whether to use a validating parser or not, or in other wordscheck the contents against the DTD specification. If this is true, warningmessages will be displayed about errors in the DTD and/or document, but the parsingwill proceed except for the presence of terminal errors.}\item{branches}{a named list of functions.Each element identifies an XML element name.If an XML element of that name is encountered inthe SAX stream, the stream is processed until theend of that element and an internal node (see\code{\link{xmlTreeParse}} and its \code{useInternalNodes} parameter)is created. The function in our branches list corresponding to thisXML element is then invoked with the (internal) node as the onlyargument.This allows one to use the DOM model on a sub-tree of the entiredocument and thus use both SAX and DOM together to get theefficiency of SAX and the simpler programming model of DOM.Note that the branches mechanism works top-down and does notwork for nested tags. If one specifies an element name in the\code{branches} argument, e.g. myNode, andthere is a nested myNode instance within a branch, the brancheshandler will not be called for that nested instance.If there is an instance where this is problematic, pleasecontact the maintainer of this package.One can cause the parser to collect a branch without identifyingthe node within the \code{branches} list. Specifically, withina regular start-element handler, one can return a functionwhose class is \code{SAXBranchFunction}.The SAX parser recognizes this and collects up the branchstarting at the current node being processed and when it iscomplete, invokes this function.This allows us to dynamically determine which nodes to treat asbranches rather than just matching names. This is necessary whena node name has different meanings in different parts of the XMLhierarchy, e.g. dict in an iTunes song list.See the file \code{itunesSax2.R} inthe examples for an example of this.This is a two step process. In the future, we might make it so thatthe R function handling the start-element event could directlycollect the branch and continue its operations without havingto call another function asynchronously.}\item{useDotNames}{a logical valueindicating whether to use thenewer format for identifying general element function handlerswith the '.' prefix, e.g. .text, .comment, .startElement.If this is \code{FALSE}, then the older formattext, comment, startElement, ...are used. This causes problems when there are indeed nodesnamed text or comment or startElement as anode-specific handler are confused with the correspondinggeneral handler of the same name. Using \code{TRUE}means that your list of handlers should have names that usethe '.' prefix for these general element handlers.This is the preferred way to write new code.}\item{error}{a function that is called when an XML error is encountered.This is called with 6 arguments and is described in \code{\link{xmlTreeParse}}.}\item{addFinalizer}{a logical value or identifier for a C routinethat controls whether we register finalizers on the intenal node.}\item{encoding}{ a character string (scalar) giving the encoding for thedocument. This is optional as the document should contain its ownencoding information. However, if it doesn't, the caller can specifythis for the parser.}}\details{This is now implemented using the libxml parser.Originally, this was implemented via the Expat XML parser byJim Clark (\url{http://www.jclark.com/}).}\value{The return value is the `handlers'argument. It is assumed that this is a closure and thatthe callback functions have manipulated variableslocal to it and that the caller knows how to extract this.}\references{\url{https://www.w3.org/XML/}, \url{http://www.jclark.com/xml/}}\author{Duncan Temple Lang}\note{The libxml parser can read URLs via http or ftp.It does not require the support of \code{wget} as usedin other parts of \R, but uses its own facilitiesto connect to remote servers.The idea for the hybrid SAX/DOM mode where we consume tokens in thestream to create an entire node for a sub-tree of the document wasfirst suggested to me by Seth Falcon at the Fred Hutchinson CancerResearch Center. It is similar to the XML::Twig module in Perlby Michel Rodriguez.}\seealso{\code{\link{xmlTreeParse}}\code{\link{xmlStopParser}}XMLParserContextFunction}\examples{fileName <- system.file("exampleData", "mtcars.xml", package="XML")# Print the name of each XML tag encountered at the beginning of each# tag.# Uses the libxml SAX parser.xmlEventParse(fileName,list(startElement=function(name, attrs){cat(name,"\n")}),useTagName=FALSE, addContext = FALSE)\dontrun{# Parse the text rather than a file or URL by reading the URL's contents# and making it a single string. Then call xmlEventParsexmlURL <- "https://www.omegahat.net/Scripts/Data/mtcars.xml"xmlText <- paste(scan(xmlURL, what="",sep="\n"),"\n",collapse="\n")xmlEventParse(xmlText, asText=TRUE)}# Using a state object to share mutable data across callbacksf <- system.file("exampleData", "gnumeric.xml", package = "XML")zz <- xmlEventParse(f,handlers = list(startElement=function(name, atts, .state) {.state = .state + 1print(.state).state}), state = 0)print(zz)# Illustrate the startDocument and endDocument handlers.xmlEventParse(fileName,handlers = list(startDocument = function() {cat("Starting document\n")},endDocument = function() {cat("ending document\n")}),saxVersion = 2)if(libxmlVersion()$major >= 2) {startElement = function(x, ...) cat(x, "\n")xmlEventParse(ff <- file(f), handlers = list(startElement = startElement))close(ff)# Parse with a function providing the input as needed.xmlConnection =function(con) {if(is.character(con))con = file(con, "r")if(isOpen(con, "r"))open(con, "r")function(len) {if(len < 0) {close(con)return(character(0))}x = character(0)tmp = ""while(length(tmp) > 0 && nchar(tmp) == 0) {tmp = readLines(con, 1)if(length(tmp) == 0)breakif(nchar(tmp) == 0)x = append(x, "\n")elsex = tmp}if(length(tmp) == 0)return(tmp)x = paste(x, collapse="")x}}\donttest{## this leaves a connection open## xmlConnection would need amending to return the connection.ff = xmlConnection(f)xmlEventParse(ff, handlers = list(startElement = startElement))}# Parse from a connection. Each time the parser needs more input, it# calls readLines(<con>, 1)xmlEventParse(ff <-file(f), handlers = list(startElement = startElement))close(ff)# using SAX 2h = list(startElement = function(name, attrs, namespace, allNamespaces){cat("Starting", name,"\n")if(length(attrs))print(attrs)print(namespace)print(allNamespaces)},endElement = function(name, uri) {cat("Finishing", name, "\n")})xmlEventParse(system.file("exampleData", "namespaces.xml", package="XML"),handlers = h, saxVersion = 2)# This example is not very realistic but illustrates how to use the# branches argument. It forces the creation of complete nodes for# elements named <b> and extracts the id attribute.# This could be done directly on the startElement, but this just# illustrates the mechanism.filename = system.file("exampleData", "branch.xml", package="XML")b.counter = function() {nodes <- character()f = function(node) { nodes <<- c(nodes, xmlGetAttr(node, "id"))}list(b = f, nodes = function() nodes)}b = b.counter()invisible(xmlEventParse(filename, branches = b["b"]))b$nodes()filename = system.file("exampleData", "branch.xml", package="XML")invisible(xmlEventParse(filename, branches = list(b = function(node) {print(names(node))})))invisible(xmlEventParse(filename, branches = list(b = function(node) {print(xmlName(xmlChildren(node)[[1]]))})))}############################################# Stopping the parser mid-way and an example of using XMLParserContextFunction.startElement =function(ctxt, name, attrs, ...) {print(ctxt)print(name)if(name == "rewriteURI") {cat("Terminating parser\n")xmlStopParser(ctxt)}}class(startElement) = "XMLParserContextFunction"endElement =function(name, ...)cat("ending", name, "\n")fileName = system.file("exampleData", "catalog.xml", package = "XML")xmlEventParse(fileName, handlers = list(startElement = startElement,endElement = endElement))}\keyword{file}\keyword{IO}