使用基于子元素值的PLSQL提取XML

时间:2013-08-19 16:28:21

标签: sql xml xpath plsql

我将以下XML文档存储在我的PLSQL过程中的clob变量中:

 <soap:Envelope>
 <soap:Body>
 <ns2:getAllIssueHistoriesResponse>
     <return>
        <eventDate>2013-08-02T11:45:58.013+02:00</eventDate>
        <eventText>Test</eventText>
        <issueEventType>
           <id>10</id>
           <value>Replied</value>
        </issueEventType>
     </return>
     <return>...</return>
     <return>...</return>
 </ns2:getAllIssueHistoriesResponse>
 </soap:Body>
 </soap:Envelope>

我正在尝试提取具有和issueEventType值为10的所有返回节点。

我已经习惯了以下代码来尝试实现这一点,其中p_xml_content是我的包含xml的clob:

 l_xml_parser := '<?xml version="1.0" encoding="UTF-8"?><results>';
 l_xml_parser := l_xml_parser ||(xmltype(p_xml_content).extract('//return/issueEventType/[id=10]').getclobval());
 l_xml_parser := l_xml_parser ||'</results>';

尽管在xml文档中肯定存在一个值为10的issueEventType,但它没有返回任何内容。

我尝试过多种变体,例如:

  l_xml_parser := l_xml_parser ||(xmltype(p_xml_content).extract('//return/issueEventType[id=''10'']').getclobval());

  l_xml_parser := l_xml_parser ||(xmltype(p_xml_content).extract('//return/issueEventType/[@id=''10'']').getclobval());

但仍然没有任何回报。

有什么想法吗?

2 个答案:

答案 0 :(得分:3)

我认为你需要对XML命名空间更加小心。

您发布的XML片段格式不正确,因为它没有soapns2命名空间的命名空间绑定。让我们添加它们,看看会发生什么:

SQL> SET SERVEROUTPUT ON
SQL>
SQL> DECLARE
  2      p_xml_content   VARCHAR2(32767) := '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns2="http://www.your-url.com/">
  3   <soap:Body>
  4   <ns2:getAllIssueHistoriesResponse>
  5       <return>
  6          <eventDate>2013-08-02T11:45:58.013+02:00</eventDate>
  7          <eventText>Test</eventText>
  8          <issueEventType>
  9             <id>10</id>
 10             <value>Replied</value>
 11          </issueEventType>
 12       </return>
 13       <return>...</return>
 14       <return>...</return>
 15   </ns2:getAllIssueHistoriesResponse>
 16   </soap:Body>
 17   </soap:Envelope>';
 18
 19   l_xml_parser       VARCHAR2(32767);
 20  BEGIN
 21   l_xml_parser := '<?xml version="1.0" encoding="UTF-8"?><results>';
 22   l_xml_parser := l_xml_parser ||(xmltype(p_xml_content).extract('//return/issueEventType[id=10]').getclobval());
 23   l_xml_parser := l_xml_parser ||'</results>';
 24
 25   dbms_output.put_line(l_xml_parser);
 26
 27  END;
 28  /
<?xml version="1.0" encoding="UTF-8"?><results><issueEventType>
  <id>10</id>

<value>Replied</value>
</issueEventType>
</results>

PL/SQL procedure successfully completed.

一切似乎都有效。我们得到了一些输出。那么你怎么没有得到任何东西?

我担心我不得不在这里猜测。鉴于您已经从XML文档中省略了两个命名空间绑定,我将假设您至少省略了一个,并且您省略的那些是将默认命名空间前缀绑定到非 - 空的URL。 (换句话说,您的XML文档具有类似xmlns="..."的属性。)如果我们将此命名空间绑定添加到您的XML文档会发生什么?

SQL> DECLARE
  2      p_xml_content   VARCHAR2(32767) := '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns2="http://www.your-url.com/" xmlns="http://www.example.com/">
  3   <soap:Body>
  4   <ns2:getAllIssueHistoriesResponse>
  5       <return>
  6          <eventDate>2013-08-02T11:45:58.013+02:00</eventDate>
  7          <eventText>Test</eventText>
  8          <issueEventType>
  9             <id>10</id>
 10             <value>Replied</value>
 11          </issueEventType>
 12       </return>
 13       <return>...</return>
 14       <return>...</return>
 15   </ns2:getAllIssueHistoriesResponse>
 16   </soap:Body>
 17   </soap:Envelope>';
 18
 19   l_xml_parser       VARCHAR2(32767);
 20  BEGIN
 21   l_xml_parser := '<?xml version="1.0" encoding="UTF-8"?><results>';
 22   l_xml_parser := l_xml_parser ||(xmltype(p_xml_content).extract('//return/issueEventType[id=10]').getclobval());
 23   l_xml_parser := l_xml_parser ||'</results>';
 24
 25   dbms_output.put_line(l_xml_parser);
 26
 27  END;
 28  /
DECLARE
*
ERROR at line 1:
ORA-30625: method dispatch on NULL SELF argument is disallowed
ORA-06512: at line 22

显然这不起作用。

这里发生的事情是对extract的调用返回NULL。您无法在getclobval()上拨打NULL,这就是ORA-30625错误的原因。对extract的调用返回NULL,因为没有任何内容符合您的XPath表达式。

XPath表达式中的非限定(即无前缀)名称始终是指“默认”命名空间中的名称。您有三个名称returnissueEventTypeid。在XPath表达式中,这些名称都在名称空间中,其URI是空字符串。在XML文档中,名称为returnissueEventTypeid的元素在URI http://www.example.com/的命名空间中。由于名称空间URI不同,因此不认为元素匹配。

解决此问题的方法是将另一个前缀绑定到与您绑定的默认前缀相同的URI,并在XPath表达式中使用该前缀。 extract方法采用可选的第二个参数,该参数指定要绑定到命名空间的其他前缀,并且其格式与您指定的用于将前缀绑定到XML中的命名空间的“属性”完全相同。而不是写

    l_xml_parser := l_xml_parser ||(xmltype(p_xml_content).extract('//return/issueEventType[id=10]').getclobval());

    l_xml_parser := l_xml_parser ||(xmltype(p_xml_content).extract('//e:return/e:issueEventType[e:id=10]', 'xmlns:e="http://www.example.com/"').getclobval());

在这里,我们将前缀e绑定到与绑定到默认前缀的URI相同的URI,然后我们将名称returnissueEventTypeid限定为这个前缀。如果您愿意,可以使用不同的前缀,只要您始终如一地使用它。

瞧,这是有效的:

SQL> DECLARE
  2      p_xml_content   VARCHAR2(32767) := '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns2="http://www.your-url.com/" xmlns="http://www.example.com/">
  3   <soap:Body>
  4   <ns2:getAllIssueHistoriesResponse>
  5       <return>
  6          <eventDate>2013-08-02T11:45:58.013+02:00</eventDate>
  7          <eventText>Test</eventText>
  8          <issueEventType>
  9             <id>10</id>
 10             <value>Replied</value>
 11          </issueEventType>
 12       </return>
 13       <return>...</return>
 14       <return>...</return>
 15   </ns2:getAllIssueHistoriesResponse>
 16   </soap:Body>
 17   </soap:Envelope>';
 18
 19   l_xml_parser       VARCHAR2(32767);
 20  BEGIN
 21   l_xml_parser := '<?xml version="1.0" encoding="UTF-8"?><results>';
 22   l_xml_parser := l_xml_parser ||(xmltype(p_xml_content).extract('//e:return/e:issueEventType[e:id=10]', 'xmlns:e="http://www.example.com/"').getclobval());
 23   l_xml_parser := l_xml_parser ||'</results>';
 24
 25   dbms_output.put_line(l_xml_parser);
 26
 27  END;
 28  /
<?xml version="1.0" encoding="UTF-8"?><results><issueEventType
xmlns="http://www.example.com/">
  <id>10</id>

<value>Replied</value>
</issueEventType>
</results>

PL/SQL procedure successfully completed.

答案 1 :(得分:0)

这里的问题是XPATH定义。有些东西正在退货,但这不是我的预期。我希望包含id的整个返回节点作为子节点。要返回此信息,需要以下内容:

  l_xml_parser := l_xml_parser ||(xmltype(p_xml_content).extract('//return/issueEventType/[id=10] /../.').getclobval());

/../。在XPATH中告诉声明当它找到并且id为10时,它需要向父级增加1级。由于当前节点是issueEventType,因此我们将转到需要的返回标记。但是,我们需要定义/。告诉XPATH在返回节点的这一点上检索当前节点。然后,这将返回正确的返回节点以及它们包含的所有节点。