Handlers DOM e SAX

Prof. Walmes Marques Zeviani

18 Abr 2017

Justificativa e objetivos

Handlers no DOM

Fundamentos

Figura  1: Nome das funções handlers para uso no DOM. Fonte: @nolan2013xml.

Figura 1: Nome das funções handlers para uso no DOM. Fonte: NOLAN; LANG (2013).

Exemplo

library(XML)

h <- xmlParse("./earth-quake.xml")
summary(h)
## $nameCounts
## 
## param event merge 
##    27     4     1 
## 
## $numNodes
## [1] 32
h
## <?xml version="1.0"?>
## <!-- O mesmo evento foi copiado para criar um arquivo minimamente
##      interessante -->
## <merge>
##   <event id="1" network-code="NC" time-stamp="2012/11/12_16:25:49 " version="0">
##     <param name="latitude" value="38.7953"/>
##     <param name="longitude" value="-122.7520"/>
##     <param name="depth" value="1.7"/>
##     <param name="magnitude" value="0.9"/>
##     <param name="magnitude-type-ext" value="Mcd = coda duration magnitude"/>
##     <param name="num-stations-mag" value="4"/>
##     <param name="stand-mag-error" value="0.0"/>
##   </event>
##   <event id="2" network-code="NC" time-stamp="2012/11/12_16:25:49 " version="0">
##     <param name="latitude" value="38.7953"/>
##     <param name="longitude" value="-122.7520"/>
##     <param name="depth" value="1.7"/>
##     <param name="magnitude" value="0.9"/>
##     <param name="magnitude-type-ext" value="Mcd = coda duration magnitude"/>
##     <param name="num-stations-mag" value="4"/>
##     <param name="stand-mag-error" value="0.0"/>
##   </event>
##   <event id="3" network-code="NC" time-stamp="2012/11/12_16:25:49 " version="0">
##     <param name="latitude" value="38.7953"/>
##     <param name="longitude" value="-122.7520"/>
##     <param name="depth" value="1.7"/>
##     <param name="magnitude" value="0.9"/>
##     <param name="magnitude-type-ext" value="Mcd = coda duration magnitude"/>
##     <param name="num-stations-mag" value="4"/>
##     <param name="stand-mag-error" value="0.0"/>
##   </event>
##   <event id="4" network-code="NC" time-stamp="2012/11/12_16:25:49 " version="0">
##     <param name="latitude" value="38.7953"/>
##     <param name="longitude" value="-122.7520"/>
##     <param name="depth" value="1.7"/>
##     <param name="magnitude-type-ext" value="Mcd = coda duration magnitude"/>
##     <param name="num-stations-mag" value="4"/>
##     <param name="stand-mag-error" value="0.0"/>
##   </event>
## </merge>
## 
# Atributos dos quais serão extraídos os valores em cada <param>.
varNames <- c("latitude", "longitude", "depth", "magnitude")

# Função que será executada sobre os elementos <param>.
paramFun <- function(node) {
    var <- xmlGetAttr(node, "name")
    if (var %in% varNames)
        values[[var]] <<- c(values[[var]], xmlGetAttr(node, "value"))
}

# Objeto vazio que será preenchido pela chamada da função paramFun().
values <- structure(replicate(length(varNames),
                              character(),
                              simplify = FALSE),
                    names = varNames)

# Faz o parse usando handlers.
xmlParse("./earth-quake.xml",
         handlers = list(param = paramFun))
## $param
## function (node) 
## {
##     var <- xmlGetAttr(node, "name")
##     if (var %in% varNames) 
##         values[[var]] <<- c(values[[var]], xmlGetAttr(node, "value"))
## }
## <bytecode: 0x6363b28>
# Tamanho dos vetores.
sapply(values, length)
##  latitude longitude     depth magnitude 
##         4         4         4         3

Lidando com os missings

# Função que vai ser executada quando passar por elementos <merge>.
mergeFun <- function(node) {
    num <- xmlSize(node)
    # Pré-aloca objeto conforme número de eventos.
    values <<- structure(replicate(length(varNames),
                                   rep(NA, num),
                                   simplify = FALSE),
                         names = varNames)
    counter <<- 0
}

# Função que vai ser executada quando passar por elementos <event>.
eventFun <- function(node) {
    counter <<- counter + 1L
}

# Função que será executada quando passar por elementos <param>.
paramFun <- function(node) {
    var <- xmlGetAttr(node, "name")
    if (var %in% varNames)
        values[[var]][counter] <<- xmlGetAttr(node, "value")
}

# parentFirst = {TRUE, FALSE}: Dos nós externos para os internos?
xmlParse("./earth-quake.xml",
         parentFirst = TRUE,
         handlers = list(param = paramFun,
                         merge = mergeFun,
                         event = eventFun))
## $param
## function (node) 
## {
##     var <- xmlGetAttr(node, "name")
##     if (var %in% varNames) 
##         values[[var]][counter] <<- xmlGetAttr(node, "value")
## }
## <bytecode: 0x3f9fce8>
## 
## $merge
## function (node) 
## {
##     num <- xmlSize(node)
##     values <<- structure(replicate(length(varNames), rep(NA, 
##         num), simplify = FALSE), names = varNames)
##     counter <<- 0
## }
## 
## $event
## function (node) 
## {
##     counter <<- counter + 1L
## }
## <bytecode: 0x38f2320>
# Resultado.
as.data.frame(values)
##   latitude longitude depth magnitude
## 1  38.7953 -122.7520   1.7       0.9
## 2  38.7953 -122.7520   1.7       0.9
## 3  38.7953 -122.7520   1.7       0.9
## 4  38.7953 -122.7520   1.7      <NA>

Implementação com closures

# Uma única função que define as funções handlers e cria os objetos que
# serão compartilhados entre todas elas. Todos, funções e objetos, são
# definidos dentro do escopo da função quakeHandlers().
quakeHandlers <- function() {
    # Cria as variáveis que serão preenchidas/incrementadas.
    counter <- 0
    values <- NULL

    # Função para elementos <param>.
    paramFun <- function(node) {
        var <- xmlGetAttr(node, "name")
        if (var %in% varNames)
            values[[var]][counter] <<- xmlGetAttr(node, "value")
    }

    # Função para elementos <event>.
    eventFun <- function(node) {
        counter <<- counter + 1L
    }

    # Função para elementos <merge>.
    mergeFun <- function(node) {
        num <- xmlSize(node)
        values <<- structure(replicate(length(varNames),
                                       rep(NA, num),
                                       simplify = FALSE),
                             names = varNames)
        counter <<- 0
    }

    # Retorno da função é um conjunto de funções.
    list(event = eventFun,
         merge = mergeFun,
         param = paramFun,
         .result = function() {
             data.frame(lapply(values, as.numeric))
         })
}

hand <- quakeHandlers()
xmlParse("./earth-quake.xml",
         parentFirst = TRUE,
         handlers = hand)
## $event
## function (node) 
## {
##     counter <<- counter + 1L
## }
## <bytecode: 0x5677f78>
## <environment: 0x52b9a78>
## 
## $merge
## function (node) 
## {
##     num <- xmlSize(node)
##     values <<- structure(replicate(length(varNames), rep(NA, 
##         num), simplify = FALSE), names = varNames)
##     counter <<- 0
## }
## <bytecode: 0x62f64c0>
## <environment: 0x52b9a78>
## 
## $param
## function (node) 
## {
##     var <- xmlGetAttr(node, "name")
##     if (var %in% varNames) 
##         values[[var]][counter] <<- xmlGetAttr(node, "value")
## }
## <bytecode: 0x55b77c8>
## <environment: 0x52b9a78>
## 
## $.result
## function () 
## {
##     data.frame(lapply(values, as.numeric))
## }
## <bytecode: 0x50b6370>
## <environment: 0x52b9a78>
hand$.result()
##   latitude longitude depth magnitude
## 1  38.7953  -122.752   1.7       0.9
## 2  38.7953  -122.752   1.7       0.9
## 3  38.7953  -122.752   1.7       0.9
## 4  38.7953  -122.752   1.7        NA

Handlers SAX

SAX

Figura  2: Nome das funções handlers para uso no SAX. Fonte: @nolan2013xml.

Figura 2: Nome das funções handlers para uso no SAX. Fonte: NOLAN; LANG (2013).

Exemplo

system("cat ./exchange-rates.xml")

saxHandlers <- function(currencies = c("USD", "NZD")) {

    # Cria os objetivos a serem preenchidos/incrementados.
    rates <- vector("list", length(currencies))
    names(rates) <- currencies
    times <- numeric()
    day <- 0

    # Função que vai operar quando encontrar tags de abertura.
    startElement <- function(name, attrs) {
        # Se o nome do elemento for diferente de "Cube" -> saia.
        if (name != "Cube") {
            return(NULL)
        }
        # Se "time" for um dos atributos.
        if ("time" %in% names(attrs)) {
            # Incremente o contador.
            day <<- day + 1
            # Passe o valor para o vetor.
            times[day] <<- attrs["time"]
            return(TRUE)
        }
        # Se "currency" for um dos atributos e tiver o valor que
        # queremos.
        if ("currency" %in% names(attrs) &&
            attrs["currency"] %in% currencies) {
            # Extraia os valores e lance nos vetores.
            rates[[attrs["currency"]]][day] <<- attrs["rate"]
        }
        return(TRUE)
    }

    # Retorno da função é uma lista com funções.
    list(startElement = startElement,
         rateData = function() {
             list(times = times,
                  rates = rates)
         })
}

sh <- saxHandlers()
xmlEventParse("./exchange-rates.xml", handlers = sh)
## $startElement
## function (name, attrs) 
## {
##     if (name != "Cube") {
##         return(NULL)
##     }
##     if ("time" %in% names(attrs)) {
##         day <<- day + 1
##         times[day] <<- attrs["time"]
##         return(TRUE)
##     }
##     if ("currency" %in% names(attrs) && attrs["currency"] %in% 
##         currencies) {
##         rates[[attrs["currency"]]][day] <<- attrs["rate"]
##     }
##     return(TRUE)
## }
## <bytecode: 0x540da50>
## <environment: 0x402c190>
## 
## $rateData
## function () 
## {
##     list(times = times, rates = rates)
## }
## <bytecode: 0x54bb270>
## <environment: 0x402c190>
exchange.rate <- sh$rateData()
exchange.rate
## $times
## [1] "2006-10-06" "2006-10-05" "2006-10-04" "2006-10-03" "2006-10-02"
## 
## $rates
## $rates$USD
## [1] "1.2664" "1.2700" "1.2626" "1.2536" "1.2901"
## 
## $rates$NZD
## [1] NA       NA       "1.2941" NA       "1.3030"

Resumo

Referências

NOLAN, D.; LANG, D. XML and Web Technologies for Data Sciences with R. Springer New York, 2013.