当标签的url部分时如何提取Oracle XML

Posted

技术标签:

【中文标题】当标签的url部分时如何提取Oracle XML【英文标题】:How to extract Oracle XML when url part of tag 【发布时间】:2020-11-03 04:25:56 【问题描述】:

我对 XML 的了解不够深入,无法知道使用什么术语来提出问题。我将混淆我试图解码的 XML sn-p,希望不会使问题无法回答。

我有一些 XML,我想获取值“要获取的第一个字符串”和“要获取的第二个字符串”。如何编写 Oracle 查询来获取它们?

<itemTypes>
<itemTypesList>
    <itemType>400SVFD2</itemType>
    <ora:itemType xmlns:ora="http://www.oracle.com/something/somethingelse" navOpt="itemTypeMaint" searchZone="C1-ITEMTYPQ" tblKeyField="ITEM_TYPE_CD">First String to Get</ora:itemType>
    <errorIfNoValue>C1NO</errorIfNoValue>
    <valueType>U</valueType>
    <valueSource>C1BF</valueSource>
    <value/>
    <billFactor>400SVFD2</billFactor>
    <ora:billFactor xmlns:ora="http://www.oracle.com/something/somethingelse" navOpt="billFactorMaint" searchZone="C1-BFQ" tblKeyField="BF_CD">Second String to Get</ora:billFactor>
    <valueAlgorithm/>
</itemTypesList>

如果我编写一个 XML 查询并放入整个标签,如果其中一些字段发生变化会发生什么?所以我认为我应该能够只指出“ora:itemType”和“ora:billFactor”,而基本上忽略其余的,对吧?

这将返回 null:

with test_table(xmldata) as (
select 
q'[
<root>
    <itemTypes>
    <itemTypesList>
        <itemType>400SVFD2</itemType>
        <ora:itemType xmlns:ora="http://www.oracle.com/something/somethingelse" navOpt="itemTypeMaint" searchZone="C1-ITEMTYPQ" tblKeyField="ITEM_TYPE_CD">First String to Get</ora:itemType>
        <errorIfNoValue>C1NO</errorIfNoValue>
        <valueType>U</valueType>
        <valueSource>C1BF</valueSource>
        <value/>
        <billFactor>400SVFD2</billFactor>
        <ora:billFactor xmlns:ora="http://www.oracle.com/something/somethingelse" navOpt="billFactorMaint" searchZone="C1-BFQ" tblKeyField="BF_CD">Second String to Get</ora:billFactor>
        <valueAlgorithm/>
    </itemTypesList>
</itemTypes>
</root>
]'
from dual
)
select
  xmlcast(
     xmlquery(
        '/root/itemTypes/itemTypesList/ora:itemType/text()'
         passing xmltype(xmldata)
         returning content
     )
     as varchar2(100)
   ) res
from test_table
;

【问题讨论】:

【参考方案1】:

第一个变体(使用带有 xpath 过滤器的 xmlquery)

with test_table(xmldata) as (
select 
q'[
<root>
    <itemTypes>
    <itemTypesList>
        <itemType>400SVFD2</itemType>
        <ora:itemType xmlns:ora="http://www.oracle.com/something/somethingelse" navOpt="itemTypeMaint" searchZone="C1-ITEMTYPQ" tblKeyField="ITEM_TYPE_CD">First String to Get</ora:itemType>
        <errorIfNoValue>C1NO</errorIfNoValue>
        <valueType>U</valueType>
        <valueSource>C1BF</valueSource>
        <value/>
        <billFactor>400SVFD2</billFactor>
        <ora:billFactor xmlns:ora="http://www.oracle.com/something/somethingelse" navOpt="billFactorMaint" searchZone="C1-BFQ" tblKeyField="BF_CD">Second String to Get</ora:billFactor>
        <valueAlgorithm/>
    </itemTypesList>
</itemTypes>
</root>
]'
from dual
)
select 
  xmlcast(
     xmlquery(
        '/root/itemTypes/itemTypesList/*:itemType[namespace-uri()="http://www.oracle.com/something/somethingelse"]/text()'
         passing xmltype(xmldata)
         returning content
     )
     as varchar2(100)
   ) res1
 ,xmlcast(
     xmlquery(
        '/root/itemTypes/itemTypesList/*:billFactor[namespace-uri()="http://www.oracle.com/something/somethingelse"]/text()'
         passing xmltype(xmldata)
         returning content
     )
     as varchar2(100)
   ) res2
from test_table
/

结果:

RES1                           RES2
------------------------------ ------------------------------
First String to Get            Second String to Get

NB 您的元素在 xmlnamespace 'ora' 中,因此您需要指定它,但由于 xmlquery 没有 XMLNAMESPACES 参数,您有 2 个选择来指定它们:

    在 xquery 的开头添加 xmlnamespace 声明:
'declare namespace ora = "http://www.oracle.com/something/somethingelse";...
    或使用函数namespace-uri()过滤元素:
*:itemType[namespace-uri()="http://www.oracle.com/something/somethingelse"]

*:element 表示您需要来自任何 xml 命名空间的 element[namespace-uri()="..."] 按名称空间过滤元素。

第二种变体:usint xmltable(xmlnamespaces(...)...)

with test_table(xmldata) as (
select 
q'[
<root>
    <itemTypes>
    <itemTypesList>
        <itemType>400SVFD2</itemType>
        <ora:itemType xmlns:ora="http://www.oracle.com/something/somethingelse" navOpt="itemTypeMaint" searchZone="C1-ITEMTYPQ" tblKeyField="ITEM_TYPE_CD">First String to Get</ora:itemType>
        <errorIfNoValue>C1NO</errorIfNoValue>
        <valueType>U</valueType>
        <valueSource>C1BF</valueSource>
        <value/>
        <billFactor>400SVFD2</billFactor>
        <ora:billFactor xmlns:ora="http://www.oracle.com/something/somethingelse" navOpt="billFactorMaint" searchZone="C1-BFQ" tblKeyField="BF_CD">Second String to Get</ora:billFactor>
        <valueAlgorithm/>
    </itemTypesList>
</itemTypes>
</root>
]'
from dual
)
select 
  xx.*
from test_table
    ,xmltable(
        xmlnamespaces('http://www.oracle.com/something/somethingelse' as "ORA", default ''),
        '/root/itemTypes/itemTypesList'
         passing xmltype(xmldata)
         columns
             res1   varchar2(100) path 'ORA:itemType/text()'
            ,res2   varchar2(100) path 'ORA:billFactor/text()'
    ) xx
;

结果:

RES1                           RES2
------------------------------ ------------------------------
First String to Get            Second String to Get

1 row selected.

还有一些基于通配符命名空间和过滤器的较短变体:

select
  xmlcast(
     xmlquery(
        '/root/itemTypes/itemTypesList/*:itemType[namespace-uri()!=""]/text()'
         passing xmltype(xmldata)
         returning content
     )
     as varchar2(100)
   ) res1
  ,xmlcast(
     xmlquery(
        '/root/itemTypes/itemTypesList/*:billFactor[2]/text()'
         passing xmltype(xmldata)
         returning content
     )
     as varchar2(100)
   ) res2
from test_table

第一个返回具有非空 namespace-uri 的元素,第二个仅返回第二个 billFactor 而不按命名空间过滤

【讨论】:

最后一个例子有现在为我工作的代码。在途中学习很多东西。【参考方案2】:

URL 是一个命名空间。您可以在 XPath 中声明它。但是ora 似乎被保留了,因为它仍然会为空,因此您可以将其更改为其他内容(仅在查询中,而不是在 XML 文档中):

        'declare namespace xyz="http://www.oracle.com/something/somethingelse";
         /root/itemTypes/itemTypesList/xyz:itemType'

所以在上下文中:

select
  xmlcast(
     xmlquery(
        'declare namespace xyz="http://www.oracle.com/something/somethingelse";
         /root/itemTypes/itemTypesList/xyz:itemType'
         passing xmltype(xmldata)
         returning content
     )
     as varchar2(100)
   ) res
from test_table

db<>fiddle

如果您想要多个值,您可能最好使用 XMLTable:

select x.*
from test_table tt
cross join xmltable(
  xmlnamespaces('http://www.oracle.com/something/somethingelse' as "xyz"),
  '/root/itemTypes/itemTypesList'
  passing xmltype(tt.xmldata)
  columns first_str varchar2(30) path 'xyz:itemType',
    second_str varchar2(30) path 'xyz:billFactor'
) x

db<>fiddle

Read more

【讨论】:

以上是关于当标签的url部分时如何提取Oracle XML的主要内容,如果未能解决你的问题,请参考以下文章

如何通过 URL 将 XML 数据提取到字符串

Oracle SUBSTR XML 解析

当页面小于窗口时如何强制HTML背景到底部

Grafana仪表板变量:当指标等于一个值时如何提取标签值

解析xml时如何检查空标签?

如何使用 re 库提取 xml 值中给定标签列表的值? [复制]