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

Posted

技术标签:

【中文标题】基于子元素值使用 PLSQL 提取 XML【英文标题】:Extract XML using PLSQL based upon child element value 【发布时间】:2013-08-19 16:28:20 【问题描述】:

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

 <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());

但仍然没有返回任何内容。

有什么想法吗?

【问题讨论】:

【参考方案1】:

我认为您需要对 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。然后你不能在NULL 上调用getclobval(),这就是给你 ORA-30625 错误的原因。对 extract 的调用返回了 NULL,因为没有任何内容与您的 XPath 表达式匹配。

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

解决此问题的方法是将另一个前缀绑定到与您绑定了默认前缀的 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,然后使用此前缀限定名称 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.

【讨论】:

恐怕这是不正确的。至少在我的情况下。命名空间在那里。请参阅下面的解决方案。这实际上是 xpath 的错误。 @jezzipin:所以你的cmets关于什么都不返回是错的吗?如果您要问的问题不能准确描述您的问题,请期待错误的答案。【参考方案2】:

这里的问题在于 XPATH 定义。一些东西被退回,但这不是我所期望的。我想要包含 id 作为子节点的整个返回节点。要返回它,需要以下内容:

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

/../.在 XPATH 中告诉语句,当它找到 10 的 id 时,它需要上一级到父级。由于当前节点是 issueEventType,这会将我们带到所需的返回标签。但是,我们需要定义 /。告诉 XPATH 在返回节点中检索当前节点。然后这将返回正确的返回节点以及它们包含的所有节点。

【讨论】:

您可以稍微简化 XPath://return[issueEventType/id=10] 也应该可以工作。

以上是关于基于子元素值使用 PLSQL 提取 XML的主要内容,如果未能解决你的问题,请参考以下文章

C# xml 反序列化不会将子元素提取到列表中

Powershell根据子节点值选择父xml节点并添加子元素

如何根据属性值确定XML元素的子元素

使用 JAXB 获取 XML 子节点的值

使用 XPath 获取子元素值,其中日期是元素值

使用 php 循环遍历 XML 元素,查找匹配并更改子元素 innerHTML