viernes, 19 de mayo de 2017

Un acercamiento a la implementación del método elementById de la clase QDomDocument de Qt

En ocasiones trabajamos con archivos xml bastante estructurados y se requiere de ciertos mecanismos para acceder de forma más eficiente a los elementos y/o nodos contenidos en estos. Un ejemplo de esto sería trabajar con HTML en el cual queremos acceder a un contenedor particular (DIV); y si hemos sido muy precavidos al momento de maquetar nuestro HTML, habremos definido ID's, si bien no para todos los DIV's (lo cual sería lo ideal, por lo de la web semántica y todo eso), por lo menos si para los DIV's más importantes y que proveen una funcionalidad muy particular.

Pues bien, si estamos trabajando con Qt e intentamos realizar una pequeña utilidad para analizar nuestro código HTML, una de las API con la que debemos trabajar sería la API xml y xmlpatterns, pero que sorpresa cuando al utilizar la clase QDomDocument, y se quiere acceder a un elemento por su ID, la documentación no muestra lo siguiente:

Returns the element whose ID is equal to elementId. If no element with the ID was found, this function returns a null element.
Since the QDomClasses do not know which attributes are element IDs, this function returns always a null element. This may change in a future version.
Devuelve el elemento cuyo ID es igual a elementId. Si no se encontró ningún elemento con el ID, esta función devuelve un elemento nulo.
Dado que las QDomClasses no saben qué atributos son identificadores de elementos, esta función devuelve siempre un elemento nulo. Esto puede cambiar en una versión futura.
http://doc.qt.io/qt-5/qdomdocument.html#elementById

Es decir, el método QDomDocument::getElementById ( const QString &elementId ), no está implementado, por esta razón, hoy traigo esta pequeña contribución que tenía mucho tiempo ya que había escrito para facilitar el acceso a elementos vía ID

QDomElement getElementById ( QString id ) {

  QDomElement elementReturned;
  QDomElement filesListNode = this->xmlDocument.firstChildElement ( "ETIQUETA_RAIZ" ).firstChild ().toElement ();
  while ( !filesListNode.isNull () ) {

    QDomNamedNodeMap attributes = filesListNode.attributes ();

    if ( attributes.namedItem ( "id" ).nodeValue () == id ) {

      elementReturned = filesListNode;
      break;
    }
    filesListNode = filesListNode.nextSibling ().toElement ();
  }
  return elementReturned;
}
Por otro lado mientras escribía esta entrada, me di cuenta que se puede hacer mucho más genérica y no tener que modificar nada, y esto sería cambiando la línea:

QDomElement filesListNode = this->xmlDocument.firstChildElement ( "ETIQUETA_RAIZ" ).firstChild ().toElement ();
por

QDomElement filesListNode = this->xmlDocument.documentElement ();
Hay que tomar en cuenta, que este método debe ser incluido en una clase que encapsule la utilización de la clase QDomDocument, donde habrá un atributo xmlDocument de tipo QDomDocument, es decir:

private:
  QDomDocument xmlDocument;
Por otro lado se pueden hacer variaciones, para hacerlo más genérico aun, como pasarle por parámetros, todos los datos requeridos para efectuar la implementación, un ejemplo sería:

QDomElement getElementById ( QDomDocument xmlDocument, QString id )
Pues bueno, la implementación les queda de tarea para la casa ;).

Hasta una nueva entrada.

No hay comentarios: