【问题标题】:XPath query works fine in online XPath tester but not in SQL ServerXPath 查询在在线 XPath 测试器中运行良好,但在 SQL Server 中却不行
【发布时间】:2017-05-15 12:25:06
【问题描述】:

我有一段 XML 必须在 SQL Server 中分解。到目前为止,我已经部分成功了,但尽管我在网上找了几个小时寻找并尝试了代码,但还是没有取得进一步进展......

在 SQL 代码下方,包括一段缩减的 XML:

DECLARE @xml xml, @hdoc int
SET @xml = '
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.egem.nl/StUF/StUF0301" xmlns:ns2="http://www.egem.nl/StUF/sector/zkn/0310" xmlns:bg="http://www.egem.nl/StUF/sector/bg/0310">
  <SOAP-ENV:Body>
    <ns2:zakLk01>
      <ns2:object ns1:entiteittype="ZAK" ns1:verwerkingssoort="T">
        <ns2:heeftBetrekkingOp ns1:entiteittype="ZAKOBJ" ns1:verwerkingssoort="T">
          <ns2:gerelateerde>
            <ns2:natuurlijkPersoon ns1:entiteittype="NPS" ns1:verwerkingssoort="I">
              <verblijfsadres xmlns="http://www.egem.nl/StUF/sector/bg/0310">
                <gor.openbareRuimteNaam>Westmalledreef 45</gor.openbareRuimteNaam>
                <wpl.woonplaatsNaam>B-16753</wpl.woonplaatsNaam>
              </verblijfsadres>
              <voornamen xmlns="http://www.egem.nl/StUF/sector/bg/0310">Erik</voornamen>
            </ns2:natuurlijkPersoon>
          </ns2:gerelateerde>
        </ns2:heeftBetrekkingOp>
      </ns2:object>
    </ns2:zakLk01>
  </SOAP-ENV:Body>
</SOAP-ENV:Envelope>
'

EXEC sp_xml_preparedocument 
@hDoc OUTPUT, 
@XML,
'<root xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
    xmlns:ns1="http://www.egem.nl/StUF/StUF0301" 
    xmlns:ns2="http://www.egem.nl/StUF/sector/zkn/0310"
    xmlns:bg="http://www.egem.nl/StUF/sector/bg/0310>"/>'


SELECT * FROM OPENXML(@hdoc, '/SOAP-ENV:Envelope') --Row Pattern
WITH 
(
Voornamen VARCHAR(50) './/ns2:heeftBetrekkingOp/ns2:gerelateerde/ns2:natuurlijkPersoon/bg:voornamen',
Adres VARCHAR(100) './/ns2:heeftBetrekkingOp/ns2:gerelateerde/ns2:natuurlijkPersoon/bg:verblijfsadres/bg:gor.openbareRuimteNaam'
)

EXEC sp_xml_removedocument @hdoc --Releasing memory

当我使用同一段 XML 尝试对 https://www.freeformatter.com/xpath-tester.html 进行两个 XPath 查询时,它们工作正常,并返回“voornamen”和“openbareRuimteNaam”的值。在 SQL 中,无论我尝试什么,它们都会返回 NULL。我一定做错了什么。谁能指出我正确的方向?

我已经尝试过 SQL Server 2014 和 2016。

【问题讨论】:

    标签: sql-server xml xpath


    【解决方案1】:

    您可以使用.value 方法代替OPENXML

    示例解决方案:

    ;WITH XMLNAMESPACES (
    'http://schemas.xmlsoap.org/soap/envelope/' AS [SOAP-ENV],
    'http://www.egem.nl/StUF/StUF0301' AS ns1,
    'http://www.egem.nl/StUF/sector/zkn/0310' AS ns2,
    'http://www.egem.nl/StUF/sector/bg/0310' AS bg
    )
    
    SELECT @xml.value('(//bg:voornamen/text())[1]','VARCHAR(50)') AS Voornamen,
    @xml.value('(//bg:gor.openbareRuimteNaam/text())[1]','VARCHAR(100)') AS Adres
    

    当然,深度搜索// 不是一个好习惯。最好指定每个节点号:

    ;WITH XMLNAMESPACES (
    'http://schemas.xmlsoap.org/soap/envelope/' AS [SOAP-ENV],
    'http://www.egem.nl/StUF/StUF0301' AS ns1,
    'http://www.egem.nl/StUF/sector/zkn/0310' AS ns2,
    'http://www.egem.nl/StUF/sector/bg/0310' AS bg
    )
    
    SELECT @xml.value('(SOAP-ENV:Envelope[1]/SOAP-ENV:Body[1]/ns2:zakLk01[1]/ns2:object[1]/ns2:heeftBetrekkingOp[1]/ns2:gerelateerde[1]/ns2:natuurlijkPersoon[1]/bg:voornamen[1]/text())[1]','VARCHAR(50)') AS Voornamen,
    @xml.value('(SOAP-ENV:Envelope[1]/SOAP-ENV:Body[1]/ns2:zakLk01[1]/ns2:object[1]/ns2:heeftBetrekkingOp[1]/ns2:gerelateerde[1]/ns2:natuurlijkPersoon[1]/bg:verblijfsadres[1]/bg:gor.openbareRuimteNaam[1]/text())[1]','VARCHAR(100)') AS Adres
    

    【讨论】:

    • 这是一个很好的建议,虽然我不会在这里使用//(深度搜索)... +1 从我这边
    • 没错,我也不会使用深度搜索。只是注意.value 方法。更好的是指定每个节点号。
    • 真:-D!告诉大家尽可能具体你是绝对正确的..快乐编码!
    【解决方案2】:

    原因

    这个 XML 是如何生成的?你遇到的问题,在这里:

    <verblijfsadres xmlns="http://www.egem.nl/StUF/sector/bg/0310">
      <gor.openbareRuimteNaam>Westmalledreef 45</gor.openbareRuimteNaam>
      <wpl.woonplaatsNaam>B-16753</wpl.woonplaatsNaam>
    </verblijfsadres>
    <voornamen xmlns="http://www.egem.nl/StUF/sector/bg/0310">Erik</voornamen>
    

    您正在声明新的默认命名空间,其 URL 与其他命名空间的 URL 相同。

    在下面的代码中,我删除了这些额外的命名空间,并使用您的代码稍作改动:voornamengor.openbareRuimteNaam 没有前缀。这有效:

    DECLARE @xml xml, @hdoc int
    SET @xml = '
    <SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" xmlns:ns1="http://www.egem.nl/StUF/StUF0301" xmlns:ns2="http://www.egem.nl/StUF/sector/zkn/0310" xmlns:bg="http://www.egem.nl/StUF/sector/bg/0310">
      <SOAP-ENV:Body>
        <ns2:zakLk01>
          <ns2:object ns1:entiteittype="ZAK" ns1:verwerkingssoort="T">
            <ns2:heeftBetrekkingOp ns1:entiteittype="ZAKOBJ" ns1:verwerkingssoort="T">
              <ns2:gerelateerde>
                <ns2:natuurlijkPersoon ns1:entiteittype="NPS" ns1:verwerkingssoort="I">
                  <verblijfsadres>
                    <gor.openbareRuimteNaam>Westmalledreef 45</gor.openbareRuimteNaam>
                    <wpl.woonplaatsNaam>B-16753</wpl.woonplaatsNaam>
                  </verblijfsadres>
                  <voornamen>Erik</voornamen>
                </ns2:natuurlijkPersoon>
              </ns2:gerelateerde>
            </ns2:heeftBetrekkingOp>
          </ns2:object>
        </ns2:zakLk01>
      </SOAP-ENV:Body>
    </SOAP-ENV:Envelope>
    '
    
    EXEC sp_xml_preparedocument 
    @hDoc OUTPUT, 
    @XML,
    '<root xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/" 
        xmlns:ns1="http://www.egem.nl/StUF/StUF0301" 
        xmlns:ns2="http://www.egem.nl/StUF/sector/zkn/0310"
        xmlns:bg="http://www.egem.nl/StUF/sector/bg/0310>"/>'
    
    
    SELECT * FROM OPENXML(@hdoc, '/SOAP-ENV:Envelope') --Row Pattern
    WITH 
    (
    Voornamen VARCHAR(50) './/ns2:heeftBetrekkingOp/ns2:gerelateerde/ns2:natuurlijkPersoon/voornamen',
    Adres VARCHAR(100) './/ns2:heeftBetrekkingOp/ns2:gerelateerde/ns2:natuurlijkPersoon/verblijfsadres/gor.openbareRuimteNaam'
    )
    
    EXEC sp_xml_removedocument @hdoc --Releasing memory
    

    分析您的 XML

    像这样尝试不使用WITH 的原始查询

    SELECT * FROM OPENXML(@hdoc, '/SOAP-ENV:Envelope')
    

    你会得到一个列表,XML 引擎如何看到你的元素(片段):

    +------------------------+-------+-----------------------------------------+------+------+----------------------------------------+
    | natuurlijkPersoon      | ns2   | http://www.egem.nl/StUF/sector/zkn/0310 | NULL | NULL | NULL                                   |
    +------------------------+-------+-----------------------------------------+------+------+----------------------------------------+
    | entiteittype           | ns1   | http://www.egem.nl/StUF/StUF0301        | NULL | NULL | NULL                                   |
    +------------------------+-------+-----------------------------------------+------+------+----------------------------------------+
    | #text                  | NULL  | NULL                                    | NULL | NULL | NPS                                    |
    +------------------------+-------+-----------------------------------------+------+------+----------------------------------------+
    | verwerkingssoort       | ns1   | http://www.egem.nl/StUF/StUF0301        | NULL | NULL | NULL                                   |
    +------------------------+-------+-----------------------------------------+------+------+----------------------------------------+
    | #text                  | NULL  | NULL                                    | NULL | NULL | I                                      |
    +------------------------+-------+-----------------------------------------+------+------+----------------------------------------+
    | verblijfsadres         | NULL  | http://www.egem.nl/StUF/sector/bg/0310  | NULL | NULL | NULL                                   |
    +------------------------+-------+-----------------------------------------+------+------+----------------------------------------+
    | xmlns                  | xmlns | NULL                                    | NULL | NULL | NULL                                   |
    +------------------------+-------+-----------------------------------------+------+------+----------------------------------------+
    | #text                  | NULL  | NULL                                    | NULL | NULL | http://www.egem.nl/StUF/sector/bg/0310 |
    +------------------------+-------+-----------------------------------------+------+------+----------------------------------------+
    | gor.openbareRuimteNaam | NULL  | http://www.egem.nl/StUF/sector/bg/0310  | NULL | NULL | NULL                                   |
    +------------------------+-------+-----------------------------------------+------+------+----------------------------------------+
    | #text                  | NULL  | NULL                                    | NULL | NULL | Westmalledreef 45                      |
    +------------------------+-------+-----------------------------------------+------+------+----------------------------------------+
    | wpl.woonplaatsNaam     | NULL  | http://www.egem.nl/StUF/sector/bg/0310  | NULL | 20   | NULL                                   |
    +------------------------+-------+-----------------------------------------+------+------+----------------------------------------+
    | #text                  | NULL  | NULL                                    | NULL | NULL | B-16753                                |
    +------------------------+-------+-----------------------------------------+------+------+----------------------------------------+
    | voornamen              | NULL  | http://www.egem.nl/StUF/sector/bg/0310  | NULL | 18   | NULL                                   |
    +------------------------+-------+-----------------------------------------+------+------+----------------------------------------+
    

    您可以看到,您要查找的元素位于命名空间中,但没有前缀...

    所有这些只是您的查询不起作用的一些解释。但是:

    您应该使用不同的方法!

    FROM OPENXML 与相应的 SP 准备和删除文档已过时,不应再使用(存在罕见的例外情况)。而是使用适当的methods the XML data type provides

    尽可能明确,最好是这样:

    ;WITH XMLNAMESPACES (
    'http://schemas.xmlsoap.org/soap/envelope/' AS [SOAP-ENV],
    'http://www.egem.nl/StUF/StUF0301' AS ns1,
    'http://www.egem.nl/StUF/sector/zkn/0310' AS ns2,
    'http://www.egem.nl/StUF/sector/bg/0310' AS bg
    )
    
    SELECT @xml.value('(SOAP-ENV:Envelope/SOAP-ENV:Body/ns2:zakLk01/ns2:object/ns2:heeftBetrekkingOp/ns2:gerelateerde/ns2:natuurlijkPersoon/bg:voornamen/text())[1]','VARCHAR(50)') AS Voornamen
          ,@xml.value('(SOAP-ENV:Envelope/SOAP-ENV:Body/ns2:zakLk01/ns2:object/ns2:heeftBetrekkingOp/ns2:gerelateerde/ns2:natuurlijkPersoon/bg:verblijfsadres/bg:gor.openbareRuimteNaam/text())[1]','VARCHAR(100)') AS Adres
    

    简单又丑陋的就是这个

    SELECT @xml.value('(//*:natuurlijkPersoon/*:voornamen/text())[1]','VARCHAR(50)') AS Voornamen
          ,@xml.value('(//*:natuurlijkPersoon/*:verblijfsadres/*:gor.openbareRuimteNaam/text())[1]','VARCHAR(100)') AS Adres
    

    【讨论】:

    • 谢谢 Paweł Tajs 和 Shnugo!我开始使用 Paweł 的回答,结果很好。在没有阅读 Shnugos 答案的情况下,我使用了明确的完整路径。我一切都做得很好,现在我有了一个可以准备测试的脚本。 XML 是由工具生成的,我无法更改它。我已经注意到标头中的一些名称空间和 XML 中的其他名称空间。 FROM OPENXML 在我几年前编写的脚本中,我决定以此为基础进行构建,但现在很明显,切换到新方法是一个很好的方法。非常感谢大家!
    猜你喜欢
    • 1970-01-01
    • 2014-12-25
    • 1970-01-01
    • 1970-01-01
    • 2019-03-16
    • 2010-12-26
    • 2019-04-14
    • 2016-10-09
    • 1970-01-01
    相关资源
    最近更新 更多