使用 SQL 获取 XML 节点中的值

Posted

技术标签:

【中文标题】使用 SQL 获取 XML 节点中的值【英文标题】:Get values in XML nodes using SQL 【发布时间】:2020-02-27 12:28:05 【问题描述】:

我在 SQL 表列中有一个 XML。我需要解码这个 xml 并获取特定节点的值。在下面找到我的 XML

<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Header/>
    <SOAP-ENV:Body>
        <ns:createTransactionResponse
            xmlns:impl="http://office/work/services/service1"
            xmlns:ns="http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd"
            xmlns:tns="http://www.erdeftq.ae/Activematrix/ESB/service1/1_0">
            <transactionResponse>
                <transaction-info>
                    <registrationId>R1234</registrationId>
                    <trialId>T12345</trialId>
                    <transactionId>12345</transactionId>
                    <transactionDate>27-02-2020:08:47</transactionDate>
                    <status>Confirmed</status>
                </transaction-info>
            </transactionResponse>
        </ns:createTransactionResponse>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>

我需要节点的值:registrationId、transactionId 和 status,我尝试了这个但没有成功,因为结果是空值:

DECLARE @xml XML
SET @xml = 'XML here'
SELECT T.C.value('@status', 'nvarchar(100)') FROM @xml.nodes('createTransactionResponse/transactionResponse/transaction-info/status') T(C)
SELECT T.C.value('@trans', 'nvarchar(100)') FROM @xml.nodes('createTransactionResponse/transactionResponse/transaction-info/transactionId') T(C)
SELECT T.C.value('@id', 'nvarchar(100)') FROM @xml.nodes('createTransactionResponse/transactionResponse/transaction-info/registrationId') T(C)

任何帮助/更正将不胜感激

【问题讨论】:

理想情况下,您应该编辑之前的问题并等待它重新打开,但是,您现在已经展示了您的尝试,这很棒。 @Larnu 如果您有一些信息,请提出解决方案。 我已经给你写了一个答案。 :) 除了使用 XML 类型还有其他方法吗?我需要 w.r.to NVARCHAR 数据类型,因为此 xml 存储在 NVARCHAR(MAX) 类型的列中 如果您不将 XML 数据存储为 xml 您不能将其视为 XML,就这么简单。修复数据类型。 【参考方案1】:

您自己的尝试是忽略命名空间并且没有指定完整的 XPath。

尝试以下方法之一:

您的 XML:

DECLARE @xml XML
SET @xml = '<SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Header/>
    <SOAP-ENV:Body>
        <ns:createTransactionResponse
            xmlns:impl="http://office/work/services/service1"
            xmlns:ns="http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd"
            xmlns:tns="http://www.erdeftq.ae/Activematrix/ESB/service1/1_0">
            <transactionResponse>
                <transaction-info>
                    <registrationId>R1234</registrationId>
                    <trialId>T12345</trialId>
                    <transactionId>12345</transactionId>
                    <transactionDate>27-02-2020:08:47</transactionDate>
                    <status>Confirmed</status>
                </transaction-info>
            </transactionResponse>
        </ns:createTransactionResponse>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>';

--这是最明确的(这总是最好的方式):

WITH XMLNAMESPACES('http://schemas.xmlsoap.org/soap/envelope/' AS n1
                  ,'http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd' AS n2)
SELECT @xml.value('(/n1:Envelope/n1:Body/n2:createTransactionResponse/transactionResponse/transaction-info/registrationId/text())[1]','nvarchar(max)') AS RegistrationId
      ,@xml.value('(/n1:Envelope/n1:Body/n2:createTransactionResponse/transactionResponse/transaction-info/transactionId/text())[1]','nvarchar(max)') AS TransactionId
      ,@xml.value('(/n1:Envelope/n1:Body/n2:createTransactionResponse/transactionResponse/transaction-info/status/text())[1]','nvarchar(max)') AS [Status];

--这将避免一些重复的 XPath,但.nodes() 会产生相当多的开销:

WITH XMLNAMESPACES('http://schemas.xmlsoap.org/soap/envelope/' AS n1
                  ,'http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd' AS n2)
SELECT ti.value('(registrationId/text())[1]','nvarchar(max)') AS RegistrationId
      ,ti.value('(transactionId/text())[1]','nvarchar(max)') AS TransactionId
      ,ti.value('(status/text())[1]','nvarchar(max)') AS [Status]
FROM @xml.nodes('/n1:Envelope/n1:Body/n2:createTransactionResponse/transactionResponse/transaction-info') A(ti);

--这是给懒人的:-)

SELECT @xml.value('(//*:registrationId)[1]','nvarchar(max)') AS RegistrationId
      ,@xml.value('(//*:transactionId)[1]','nvarchar(max)') AS TransactionId
      ,@xml.value('(//*:status)[1]','nvarchar(max)') AS [Status];

提示:最后一个(懒人)使用深度搜索(使用//)并使用通配符作为命名空间。如果元素可能在您的 XML 中多次出现,这是非常危险的。

【讨论】:

有没有使用 VARCHAR 类型而不是 XML 数据类型?请帮忙 @AdityaPrasad 以字符串类型存储 XML 是一种非常糟糕的方法。如果可能的话,你应该改变它。如果没有,您将不得不转换您的字符串。在这种情况下,我建议对.nodes() 使用第二种方法,它只需要一次演员表。 好答案,我这边+1!【参考方案2】:

您拥有的 XML 非常复杂。您有多个命名空间,不同的节点使用不同的命名空间。这意味着您需要使用WITH XMLNAMESPACES 来声明所有这些。

然后您需要使用nodes 导航到所需的节点,并在它们前面加上适当的命名空间,直到您到达transaction-info。然后使用value 获取信息。

@Status 不是你想要的,那是因为你有类似&lt;node status=1\&gt; 的东西,你需要获取节点的text() 值。

结果如下:

DECLARE @X xml = '<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope
    xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
    <SOAP-ENV:Header/>
    <SOAP-ENV:Body>
        <ns:createTransactionResponse
            xmlns:impl="http://traffic2/traffic/services/service1"
            xmlns:ns="http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd"
            xmlns:tns="http://www.abc.ae/Activematrix/ESB/service1/1_0">
            <transactionResponse>
                <transaction-info>
                    <registrationId>R1234</registrationId>
                    <trialId>T12345</trialId>
                    <transactionId>12345</transactionId>
                    <transactionDate>27-02-2020:08:47</transactionDate>
                    <status>Confirmed</status>
                </transaction-info>
            </transactionResponse>
        </ns:createTransactionResponse>
    </SOAP-ENV:Body>
</SOAP-ENV:Envelope>';

WITH XMLNAMESPACES ('http://schemas.xmlsoap.org/soap/envelope/' AS [SOAP-ENV],
                    'http://traffic2/traffic/services/service1' AS impl, --YOu don't use this in the XML, but incldued anyway, for completeness
                    'http://www.regfrez.com/schemas/service1_V2/SharedResources/XMLSchema/Schema.xsd' AS ns,
                    'http://www.abc.ae/Activematrix/ESB/service1/1_0' AS tns) --YOu don't use this in the XML, but incldued anyway, for completeness
SELECT ti.value('(status/text())[1]','varchar(10)') AS [Status],
       ti.value('(transactionId/text())[1]','int') AS Trans,
       ti.value('(registrationId/text())[1]','varchar(10)') AS ID
FROM @X.nodes('SOAP-ENV:Envelope/SOAP-ENV:Body/ns:createTransactionResponse/transactionResponse/transaction-info') N(ti);

【讨论】:

以上是关于使用 SQL 获取 XML 节点中的值的主要内容,如果未能解决你的问题,请参考以下文章

使用 JAXB 获取 XML 子节点的值

使用 foreach 获取 XML 节点的值

3 尝试使用 C# 删除 XML 节点

java如何读取xml节点元素值?

获取xml节点的值并使用as3对其进行操作

js怎么获取xml里某个节点的值并输出