2009-04-13 16 views
6

¿Alguien sabe cómo recuperar los valores de <ZIPCODE> y <CITY> usando PL/SQL? He seguido un tutorial en la red, sin embargo, puede recuperar los nombres de los elementos, pero no sus valores. ¿Alguno de ustedes sabe lo que parece ser el problema? Ya he consultado Google (secreto bien guardado de internet) sobre esto, pero sin suerte :(Recuperar el valor de un elemento xml en Oracle PL SQL

<Zipcodes> 
    <mappings Record="4"> 
    <STATE_ABBREVIATION>CA</STATE_ABBREVIATION> 
    <ZIPCODE>94301</ZIPCODE> 
    <CITY>Palo Alto</CITY> 
    </mappings> 
</Zipcodes> 

aquí está el código de ejemplo:

-- prints elements in a document 
PROCEDURE printElements(doc DBMS_XMLDOM.DOMDocument) IS 
    nl DBMS_XMLDOM.DOMNodeList; 
    n DBMS_XMLDOM.DOMNode; 
    len number; 
BEGIN 
    -- get all elements 
    nl := DBMS_XMLDOM.getElementsByTagName(doc, '*'); 

    len := DBMS_XMLDOM.getLength(nl); 

    -- loop through elements 
    FOR i IN 0 .. len - 1 LOOP 
     n := DBMS_XMLDOM.item(nl, i); 

     testr := DBMS_XMLDOM.getNodeName(n) || ' ' || DBMS_XMLDOM.getNodeValue(n); 

     DBMS_OUTPUT.PUT_LINE (testr); 
    END LOOP; 

    DBMS_OUTPUT.PUT_LINE (''); 
END printElements; 
+0

Personalmente, prefiero usar XMLType y usar la función Extraer para obtenerlos a través de XPath. p.ej. 'myxml.Extract ('/ Zipcodes/mappings/ZIPCODE/text()');' - debe ser más simple que recorrer el DOM. –

Respuesta

12

Es necesario cambiar la línea

testr := DBMS_XMLDOM.getNodeName(n) || ' ' || DBMS_XMLDOM.getNodeValue(n); 

a

testr := DBMS_XMLDOM.getNodeName(n) || ' ' || DBMS_XMLDOM.getNodeValue(DBMS_XMLDOM.getFirstChild(n)); 

In XML DOM, los elementos no tienen ningún 'valor' para hablar. Los nodos de elemento contienen nodos de texto como secundarios, y son estos nodos los que contienen los valores que desea.

EDITAR (en respuesta al comentario de Tomalak): No conozco ninguna función en DBMS_XMLDOM para obtener el valor combinado de todos los nodos de texto secundarios de un elemento. Si eso es lo que necesita, entonces es muy posible que tenga que usar algo como la siguiente función:

CREATE OR REPLACE FUNCTION f_get_text_content (
    p_node   DBMS_XMLDOM.DOMNode 
) RETURN VARCHAR2 
AS 
    l_children  DBMS_XMLDOM.DOMNodeList; 
    l_child   DBMS_XMLDOM.DOMNode; 
    l_text_content VARCHAR2(32767); 
    l_length   INTEGER; 
BEGIN 
    l_children := DBMS_XMLDOM.GetChildNodes(p_node); 
    l_length := DBMS_XMLDOM.GetLength(l_children); 
    FOR i IN 0 .. l_length - 1 LOOP 
    l_child := DBMS_XMLDOM.Item(l_children, i); 
    IF DBMS_XMLDOM.GetNodeType(l_child) IN (DBMS_XMLDOM.TEXT_NODE, DBMS_XMLDOM.CDATA_SECTION_NODE) THEN 
     l_text_content := l_text_content || DBMS_XMLDOM.GetNodeValue(l_child); 
    END IF; 
    END LOOP; 
    RETURN l_text_content; 
END f_get_text_content; 
/
+0

¿Qué sucede si un nodo contiene varios descendientes? ¿Cómo se obtiene el texto combinado? – Tomalak

+2

Gracias por tomarse el tiempo, ya di mi +1. Sin embargo, ¿no debería ser esto recursivo de alguna manera? ¿Quizás XPath es una opción más apropiada? (Es cierto que no sé mucho sobre Oracle, así que no tengo idea de lo que se requiere para hacer consultas XPath.) – Tomalak

+0

La función anterior se puede hacer fácilmente recursiva agregando una cláusula ELSIF al bloque IF. Si vale la pena hacerlo depende de lo que necesite el OP. XPath es posible, sin embargo, colegas míos han tenido problemas de confiabilidad con Oracle XML DB (especialmente con XSLT), así que prefiero no ir por ese camino. –

0

Este es un ejemplo sencillo de cómo recuperar los valores deseados del documento:

declare 
    vDOM  dbms_xmldom.DOMDocument; 
    vNodes dbms_xmldom.DOMNodeList; 
    vXML  xmltype := xmltype('<Zipcodes> 
    <mappings Record="4"> 
    <STATE_ABBREVIATION>CA</STATE_ABBREVIATION> 
    <ZIPCODE>94301</ZIPCODE> 
    <CITY>Palo Alto</CITY> 
    </mappings> 
</Zipcodes>'); 
begin 
    -- create the dom document from our example xmltype 
    vDOM := dbms_xmldom.newDOMDocument(vXML); 
    -- find all text nodes in the dom document and return them into a node list 
    vNodes := dbms_xslprocessor.selectNodes 
       (n   => dbms_xmldom.makeNode(dbms_xmldom.getDocumentElement(vDOM)) 
       ,pattern => '//*[self::ZIPCODE or self::CITY]/text()' 
       ,namespace => null 
      ); 
    -- iterate through the node list 
    for i in 0 .. dbms_xmldom.getlength(vNodes) - 1 loop 
    -- output the text value of each text node in the list 
    dbms_output.put_line(dbms_xmldom.getNodeValue(dbms_xmldom.item(vNodes,i))); 
    end loop; 
    -- free up document resources 
    dbms_xmldom.freeDocument(vDOM);  
end; 

El encima de los resultados en la salida solicitada:

94301 
Palo Alto 

Sustitución del patrón XPath en el ejemplo anterior con el patrón => '// texto()' resulta en la salida:

CA 
94301 
Palo Alto 

ie. todo el texto en el documento. Muchas variaciones en este tema son por supuesto posibles usando esta técnica.

Cuestiones relacionadas