Probar nodos de contexto

Ahora realizaremos un test al nodo de contexto seleccionado anteriormente, para de esta manera poder generar un aviso de error. Como se puede comprobar en el ejemplo, con el elemento <report> podemos definir un test. La estructura básica de un test es sencilla:

<report test="Expresión XPath">Aviso de error</report>

Si se comprueba un error, aparecerá el contenido del elemento <report> como aviso de error. 

La expresión XPath en el atributo test genera un valor booleano como valor de salida. Si en XPath no se devuelve un valor booleano, el analizador sintáctico intentará convertir en valor booleano el resultado obtenido. Por ejemplo, una expresión que devuelva como resultado uno o varios nodos recibirá el valol booleano true, o bien, si la expresión devuelve un conjunto de nodos vacío, se convertirá en el valor false.

<schema xmlns="http://purl.oclc.org/dsdl/schematron"> 
  <ns uri="http://www.schematron.info/arche" prefix="arc"/> 
  <pattern> 
    <rule context="arc:animal[@carnivoro='sí']"> 
      <report test="parent::*/arc:animal[@carnivoro='no']"> 
        Hay animales carnívoros y herbívoros en un mismo compartimento. 
        ¡Los animales no pueden ser utilizados como fuente de comida!
      </report> 
    </rule> 
  </pattern> 
</schema>

En el test anterior se busca un elemento <animal> que tenga un atributo carnivoro='no'. En caso de que haya uno o varios elementos que cumplan esa condición, el resultado de la expresión XPath se transformará en el valor true, en caso contrario se devolverá el valor false.
El elemento <report> permite comprobar si existe un error. Si la expresión XPath en el atributo test devuelve el valor true (desde el punto de vista lógico), será la indicación de que se ha producido un error, por lo que la implementación mostrará un aviso de error. El valor true es por lo tanto un indicador de errores. De forma alternativa es posible en Schematron utilizar el elemento <assert>. La construcción del test en este elemento es idéntica a la del elemento <report>:

<assert test="Expresión XPath">aviso de error</assert>

La diferencia entre los elementos <report> y <assert > estriba en el tratamiento del valor booleano. El elemento <assert> define una aseveración para un nodo de contexto que debe ser cierta. El valor false de la expresión XPath indica que la aseveración no es cierta y se genera un mensaje de error. El indicador de errores es en este caso el valor false.
El usuario familiarizado con XPath comprobará que la distinción entre <report> y <assert > no es realmente necesaria, ya que cualquier test puede expresarse con cualquiera de los dos elementos mediante una sencilla transformación. Las dos reglas a continuación muestran el funcionamiento idéntico de ambos elementos:

<rule context="arc:animal[@carnivoro='sí']"> 
  <report test="parent::*/arc:animal[@carnivoro='no']"> 
    Hay animales carnívoros y herbívoros en un mismo compartimento. 
    ¡Los animales no pueden ser utilizados como fuente de comida!
  </report> 
</rule> 
<rule context="arc:animal[@carnivoro='sí']"> 
  <assert test="not(parent::*/arc:animal[@carnivoro='no'])"> 
    Hay animales carnívoros y herbívoros en un mismo compartimento. 
    ¡Los animales no pueden ser utilizados como fuente de comida!
  </assert> 
</rule>

Mediante la función XPath not() es posible convertir los tests definidos con el elemento <report> a tests definidos con <assert> y viceversa. ¿Por qué existe entonces esta distinción? Por un lado por motivos semánticos, por otro, porque es más práctico.
Desde el punto de vista semántico Schematron puede distinguir entre un error y una aseveración. Esto nos puede llevar a diferentes presentaciones de los avisos de error según la implementación que se esté utilizando. Se distingue pues entre los siguientes enunciados:

• Error (<report>): El nodo de contexto no debe cumplir la condición.
• Aseveración (<assert>): El nodo de contexto debe cumplir la condición.

Es práctico además hacer uso de estos dos elementos, ya que así se evita el uso de la función XPath not(), que en caso contrario sería necesario. El desarrollador puede, por otro lado, decidirse por la definición de tests mediante <assert> o <report>, o bien, mediante una mezcla de ambos. Con los conocimientos que ahora poseemos sobre Schematron podemos comprender mejor la regla de nuestro ejemplo:

<schema xmlns="http://purl.oclc.org/dsdl/schematron"> 
  <ns uri="http://www.schematron.info/arche" prefix="arc"/> 
  <pattern> 
    <rule context="arc:animal[@carnivoro='sí']"> 
      <report test="parent::*/arc:animal[@carnivoro='no']"> 
        Hay animales carnívoros y herbívoros en un mismo compartimento. 
        ¡Los animales no pueden ser utilizados como fuente de comida!
      </report> 
    </rule> 
  </pattern> 
</schema>

La regla se aplica a todos los elementos <animal> que tengan un atributo carnivoro con el valor . Para esta regla se ha definido el siguiente test: El elemento padre del elemento <animal> en cuestión no deberá contener otro elemento <animal> cuyo atributo carnivoro tenga un valor no.
Partiendo de la estructura de nuestro documento esquema podemos comprobar que los animales se encuentran alojados en compartimentos. El elemento padre de un elemento <animal> es siempre un elemento <compartimento>. Si el elemento padre <compartimento> de un elemento de contexto <animal> contiene otro elemento <animal> cuyo atributo sea carnivoro='no', esto conlleva que el elemento es hermano del elemento de contexto y que por tanto ambos se encuentran en el mismo compartimento. Puesto que en nuestra regla el elemento de contexto siempre es un animal carnívoro, la distribución de los animales en un compartimento determinado va a contradecir la regla del arca tan pronto como el test definido en <report> encuentre un animal herbívoro.
Una vez que se ha seleccionado un nodo, es posible realizar varios tests con el mismo, en lugar de un solo test como en el ejemplo anterior. Así, vamos a ampliar nuestro ejemplo con una regla adicional: Los carnívoros no sólo deben permanecer alejados de los herbívoros, sino también de los carnívoros que sean más débiles. ¡Los carnívoros más fuertes pueden hincarle el diente a los más pequeños! La fuerza de un animal se determinará en función del peso del animal. Deberá pues observarse, que el peso de un carnívoro no supere el doble del peso de su compañero de compartimento:

<pattern> 
  <rule context="arc:animal[@carnívoro='sí']"> 
    <report test="parent::*/arc:animal[@carnívoro='no']"> 
      Hay animales carnívoros y herbívoros en un mismo compartimento. 
      ¡Los animales no pueden ser utilizados como fuente de comida! 
    </report> 
    <report test="parent::*/arc:animal/arc:peso &lt; (arc:peso div 2)"> 
      Noé, este animal carnívoro es muy fuerte para su compañero. 
      Podría usarlo como fuente de comida.
    </report> 
  </rule> 
</pattern>  

La expresión XPath comprueba si existe un hermano (parent::*/arc:animal) con un elemento hijo <peso> cuyo valor sea menor (&lt;) que la mitad del valor del elemento <peso> del nodo de contexto (arc:peso div 2).
En caso de que se deban realizar varios tests al nodo de contexto, los elementos con los tests se dispondrán uno tras otro. Aquí se podrán mezclar libremente elementos <assert >- y <report>. Si se encuentra en la instancia un nodo de contexto adecuado, se aplicarán al mismo todos los tests. El orden de los tests no juega ningún papel, con excepción de la visualización del aviso de error.

Nota para expertos en XSLT:

En la transformación "Skeleton" a XSLT se convierten los tests a construcciones con if o choose. El test con report:

<report test="parent::*/arc:animal[@carnivoro='no']"> Hay carnívoros y herbívoros en un mismo compartimento. ¡Los animales no pueden ser utilizados como fuente de comida!</report>

se convierte en:

<xsl:if test="arc:animal[@carnivoro='sí']">
   […]
   <!-- Implementación del aviso de error en formato SVRL -->
   […]
</xsl:if>

Por el contrario, si se realiza el test con assert se recurre al elemento <xsl:choose>:

<assert test="not(parent::*/arc:animal[@carnivoro='no'])">Hay carnívoros y herbívoros en un mismo compartimento. ¡Los animales no pueden ser utilizados como fuente de comida!</assert>

se convierte en:

<xsl:choose> 
   <xsl:when test="not(parent::*/arc:animal[@carnivoro='no'])"/> 
   <xsl:otherwise> 
      […] 
      <!-- Implementación del aviso de error en formato SVRL --> 
      […] 
   </xsl:otherwise> 
</xsl:choose>

Derechos de autor © dpunkt.verlag GmbH 2011
El usuario podrá imprimir la versión online. La copia será exclusivamente para uso personal. Por lo demás el presente capítulo del libro publicado en lengua alemana "Schematron - Effiziente Business Rules für XML-Dokumente" está sometido a los mismos términos y condiciones que la versión impresa. La presente obra está protegida en su totalidad por la ley de propiedad intelectual. Reservados todos los derechos, incluyendo los derechos de reproducción, traducción, microfilmación, así como el almacenamiento y procesamiento en sistemas electrónicos.

dpunkt.verlag GmbH, Ringstraße 19B, 69115 Heidelberg, teléfono + 49 (0) 6221 14830, fax + 49 (0) 6221 148399, hallo(at)dpunkt.de