2012-04-25 18 views
6

Digamos que quiero comparar dos objetos DOMDocument. Tienen el mismo contenido, pero el orden y el formato pueden estar desactivados. Por ejemplo, primero da salida a este XML:¿Cómo comparar XML similares con PHPUnit?

<responses> 
    <response id="12"> 
     <foo>bar</foo> 


<lorem>ipsum</lorem> 
      <sit>dolor</sit> 

    </response></responses> 

otro salidas:

<responses> 
<response id="12"> 

      <lorem>ipsum</lorem><sit>dolor</sit> 
     <foo>bar</foo> 
          </response> 
</responses> 

Como se puede ver, que contienen la misma estructura XML pero algunos elementos podrían estar en diferentes orden y el formato es completamente aleatorio

Si hago:

$this->assertEquals(); 

La prueba, por supuesto, falle. No quiero probar solo la estructura XML, sino también los contenidos.

¿Alguna idea?

+0

¿qué tal eliminar todos los espacios en blanco y luego comparar los hash sha1? –

Respuesta

3
+1

Si bien esto puede responder teóricamente a la pregunta, [sería preferible] (http://meta.stackexchange.com/q/8259) incluir aquí las partes esenciales de la respuesta y proporcionar el enlace de referencia. – Nanne

+1

De hecho, el enlace ahora está roto, por lo que esta respuesta es inútil en la actualidad. – nIcO

+0

@nIcO corrigió el enlace roto –

3

¿Qué versión de PHPUnit es esto? Estoy bastante seguro de que las versiones recientes son compatibles con las comparaciones de DomDocument.

Versión corta: utilice la configuración $doc->preserveWhiteSpace para eliminar el espacio en blanco, y luego use $doc->C14N() para quitar los comentarios y obtener una cadena que puede comparar.


OK, aquí está un script se puede jugar con, tenga en cuenta que las líneas EOD; no pueden tener al final, o espacios iniciales.

$x1 = <<<EOD 
<responses> 
    <response id="12"> 
     <foo>bar</foo> 

<lorem>ipsum</lorem> 
      <sit>dolor</sit> 
     <!--This is a comment --> 

    </response></responses> 
EOD; 

$x2 = <<<EOD 
<responses> 
<response id="12"> 

      <lorem>ipsum</lorem><sit>dolor</sit> 
     <foo>bar</foo> 
     <!--This is another comment --> 
          </response> 
</responses> 
EOD; 

// El siguiente bloque es parte del mismo archivo, sólo estoy haciendo de este formato a la ruptura de modo que el sistema de resaltado de sintaxis stackoverflow no se ahogue.

$USE_C14N = true; // Try false, just to see the difference. 

$d1 = new DOMDocument(1.0); 
$d2 = new DOMDocument(1.0); 

$d1->preserveWhiteSpace = false; 
$d2->preserveWhiteSpace = false; 

$d1->formatOutput = false; // Only useful for "pretty" output with saveXML() 
$d2->formatOutput = false; // Only useful for "pretty" output with saveXML() 

$d1->loadXML($x1); // Must be done AFTER preserveWhiteSpace and formatOutput are set 
$d2->loadXML($x2); // Must be done AFTER preserveWhiteSpace and formatOutput are set 

if($USE_C14N){ 
    $s1 = $d1->C14N(true, false); 
    $s2 = $d2->C14N(true, false); 
} else { 
    $s1 = $d1->saveXML(); 
    $s2 = $d2->saveXML(); 
} 

echo $s1 . "\n"; 
echo $s2 . "\n"; 

salida con $USE_C14N=true;

<responses><response id="12"><foo>bar</foo><lorem>ipsum</lorem><sit>dolor</sit></response></responses> 
<responses><response id="12"><lorem>ipsum</lorem><sit>dolor</sit><foo>bar</foo></response></responses> 

salida con $USE_C14N=false;

<?xml version="1.0"?> 
<responses><response id="12"><foo>bar</foo><lorem>ipsum</lorem><sit>dolor</sit><!--This is a comment --></response></responses> 

<?xml version="1.0"?> 
<responses><response id="12"><lorem>ipsum</lorem><sit>dolor</sit><foo>bar</foo><!--This is another comment --></response></responses> 

Tenga en cuenta que $doc->C14N() podría ser más lento, pero creo que es probable que despojar a los comentarios es deseable. Tenga en cuenta que todo esto también asume que el espacio en blanco en su XML no es importante, y es probable que haya algunos casos de uso donde esa suposición no es correcta ...

1

Le sugiero que convierta el XML en DOMDocuments y luego use assertEquals con aquellos. Ya es compatible con PHPUnit. Sin embargo, es posible que ya no cubra todas sus necesidades.

Se puede volver a dar formato a los documentos y volver a cargarlos, así, ver PHP XML how to output nice format:

$doc->preserveWhiteSpace = false; 
$doc->formatOutput = true; 

Otra idea es ordenar a continuación, los niños por su nombre de código - ni idea de si eso se ha hecho antes.

0

Puede utilizar assertXmlFileEqualsXmlFile de PHPUnit(), assertXmlStringEqualsXmlFile() y assertXmlStringEqualsXmlString() funciones; sin embargo, no dan información sobre lo que es diferente, que sólo permiten la prueba falla con

Failed asserting that two DOM documents are equal. 

Así que es posible que desee utilizar PHP de XMLDiff PECL extension, o escribir su propia función de comparación recursiva. Si el tiempo importa, recomendaría no usar DOM sino SimpleXML en su lugar debido a la API más simple.

0

He estado jugando con algunas de las nociones presentadas aquí y pensé que podría publicar mi resultado final. Una de las cosas que quería poder hacer era comparar los resultados de dos nodos o dos documentos. (técnicamente, este puede comparar uno o el segundo hijo de un documento similar se compara con otro)

Básicamente, si envío un DomDocument, lo clona usando $ clone-> loadXml ($ obj -> saveXml) pero si se trata de un nodo enviado, hace un $ clone-> importNode ($ obj); El orden de if es importante porque DomDocument también es una instancia de DomNode.

/** 
* @param \DOMDocument|\DOMNode $n1 
* @param \DOMDocument|\DOMNode $n2 
* @return bool 
* @throws \Exception for invalid data 
*/ 
function compareNode($n1, $n2) 
{ 
    $nd1 = new \DOMDocument('1.0', "UTF-8"); 
    if ($n1 instanceof \DOMDocument) { 
     $nd1 = $n1->cloneNode(true); 
     $nd1->preserveWhiteSpace = false; 
     $nd1->formatOutput = false; 
     $nd1->loadXML($n1->saveXML()); 
    } elseif ($n1 instanceof \DOMNode) { 
     $nd1->preserveWhiteSpace = false; 
     $nd1->formatOutput = false; 
     $nd1->importNode($n1); 
    } else { 
     throw new \Exception(__METHOD__ . " node 1 is invalid"); 
    } 

    $nd2 = new \DOMDocument('1.0', "UTF-8"); 
    if ($n2 instanceof \DOMDocument) { 
     $nd2 = $n2->cloneNode(true); 
     $nd2->preserveWhiteSpace = false; 
     $nd2->formatOutput = false; 
     $nd2->loadXML($n2->saveXML()); 
    } elseif ($n1 instanceof \DOMNode) { 
     $nd2->preserveWhiteSpace = false; 
     $nd2->formatOutput = false; 
     $nd2->importNode($n2); 
    } else { 
     throw new \Exception(__METHOD__ . " node 2 is invalid"); 
    } 

    return ($nd1->C14N(true, false) == $nd2->C14N(true, false)); 
} 
Cuestiones relacionadas