使用 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
不是你想要的,那是因为你有类似<node status=1\>
的东西,你需要获取节点的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 节点中的值的主要内容,如果未能解决你的问题,请参考以下文章