2009-04-13 3 views
6

Кто-нибудь знает, как получить значения <ZIPCODE> и <CITY> с использованием PL/SQL? Я следил за учебником по сети, однако он может извлекать имена элементов, но не их значения. Кто-нибудь из вас знает, какая проблема? Я уже консультировался Google (хорошо держится в секрете Интернета) по этому поводу, но не повезло :(Извлечь значение элемента xml в Oracle PL SQL

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

вот пример кода:

-- 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

Лично я предпочитаю использовать XMLType и использовать функцию Extract для их получения через XPath. например 'myxml.Extract ('/ Zipcodes/mappings/ZIPCODE/text()');' - должен быть проще, чем ходить по DOM. –

ответ

12

Вам нужно изменить строку

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

до

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

В XML DOM, элементы не имеют никакой «ценности», о которых можно говорить. Узлы элементов содержат текстовые узлы в качестве дочерних элементов, и именно эти узлы содержат нужные значения.

EDIT (в ответ на комментарий Томалака): Я не знаю каких-либо функций в DBMS_XMLDOM, чтобы получить объединенное значение всех дочерних текстовых узлов элемента. Если это то, что вам нужно, то вы можете также нужно использовать что-то вроде следующей функции:

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

Что делать, если узел содержит несколько потомков - как вы получаете их комбинированный текст? – Tomalak

+2

Спасибо, что нашли время, я уже отдал свой +1 ранее. Однако - разве это не должно быть рекурсивным? Может быть, XPath - более подходящий выбор? (Я, по общему признанию, мало знаю об Oracle, поэтому я понятия не имею, что нужно делать с запросами XPath.) – Tomalak

+0

Вышеуказанная функция может быть легко сделана рекурсивной, добавив предложение ELSIF в блок IF. Независимо от того, стоит ли это делать, зависит от потребностей ОП. XPath возможен, однако у моих коллег были проблемы с безопасностью с Oracle XML DB (особенно с XSLT), поэтому я бы не пошел так. –

0

Это простой пример того, как получить желаемые значения из документа:

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; 

выше, приводит к выходу запрашиваемых:

94301 
Palo Alto 

Замена шаблона XPATH в приведенном выше примере с рисунком => «// текст()» результаты в выходе:

CA 
94301 
Palo Alto 

ie. весь текст в документе. Разумеется, многие варианты этой темы можно использовать с использованием этой техники.