【问题标题】:Can't parse XML with outer apply无法使用外部应用解析 XML
【发布时间】:2021-12-10 11:53:07
【问题描述】:

我在表中有一个 XML 列,我试图将值从平面表结构中解析出来。

我正在尝试在此处输入 XML,但 stackoverflow 将其设置为代码,当我尝试将其格式化为代码时,它仍然不接受它。

我什至无法从“标题”级别获取数据。

<RequestMessage xmlns="http://iec.ch/TC57/2011/schema/message" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="Message.xsd">
  <Header>
    <Verb>created</Verb>
    <Noun>MeterReadings</Noun>
    <Timestamp>2021-03-08T00:57:18+01:00</Timestamp>
    <Source>Ipsum Lorum</Source>
    <AsyncReplyFlag>true</AsyncReplyFlag>
    <AckRequired>true</AckRequired>
    <MessageID>Ipsum Lorum</MessageID>
    <CorrelationID />
  </Header>
  <Payload>
    <MeterReadings xmlns:MeterReadings="http://iec.ch/TC57/2011/MeterReadings#" xmlns="http://iec.ch/TC57/2011/MeterReadings#">
      <MeterReading>
        <IntervalBlocks>
          <IntervalReadings>
            <timeStamp>2021-03-07T01:00:00+01:00</timeStamp>
            <value>480.196</value>
            <ReadingQualities>
              <ReadingQualityType ref="3.0.0" />
            </ReadingQualities>
          </IntervalReadings>
          <IntervalReadings>
            <ReadingType ref="11.0.7.3.1.2.12.1.1.0.0.0.0.101.0.3.72.0" />
          </IntervalReadings>
        </IntervalBlocks>
        <Meter>
          <mRID>0000000000000</mRID>
          <status>
            <remark>Ipsum Lorum</remark>
            <value>ESP</value>
          </status>
        </Meter>
        <UsagePoint>
          <mRID>73599900000000</mRID>
        </UsagePoint>
      </MeterReading>
    </MeterReadings>
  </Payload>
</RequestMessage>

我无法解析它,我尝试使用其他线程的示例。我试图不使用 OPENXML 解决方案,因为需要 DECLARE 并执行内置过程以定期从内存中清除 XML。我正在尝试使用 OUTER APPLY 解决方案。 就像 How to parse XML data in SQL server tableQuery XML with nested nodes on Cross Apply 中的 Shugos 解决方案。

它不起作用。

时间戳列返回null。

select 
t.file_created_time
,c.value('(Timestamp)[1]','varchar(max)') as timestamp
from load.t t
OUTER APPLY t.xml_data.nodes('RequestMessage/Header') as m(c)

【问题讨论】:

  • 正如@marc_s 已经提到的,XML 格式不正确。
  • 提问时,您需要提供minimal reproducible example: (1) DDL 和样本数据填充,即 CREATE 表和 INSERT T-SQL 语句。 (2) 你需要做什么,即逻辑和你的代码尝试在 T-SQL 中实现它。 (3) 期望的输出,基于上述#1 中的样本数据。 (4) 您的 SQL Server 版本 (SELECT @@version;)。
  • @YitzhakKhabinsky 感谢您提供有关我应该在帖子中包含的内容的信息。我认为 XML 文本和我正在运行的 sql 代码就足够了,但我知道有些人可能使用的是非常旧版本的 sql server,所以我会认为将来会这样。
  • 现在我了解命名空间的问题是我搜索了一下并发现例如 stackoverflow.com/questions/22818591/… 和评论 vittore 似乎暗示不需要使用 WITH 和命名空间声明的问题。相反,可以只接受任何命名空间。但是我还没有让它工作,但下一步很有趣。我有动力尝试在没有 CTE 的情况下工作。

标签: sql sql-server xml tsql xquery


【解决方案1】:

请尝试以下解决方案。

从 SQL Server 2005 开始,最好使用基于 w3c 标准的 XQuery 语言,同时处理 XML 数据类型。

保留 Microsoft 专有的 OPENXML 及其伙伴 sp_xml_preparedocumentsp_xml_removedocument 只是为了与过时的 SQL Server 2000 向后兼容。它们的使用已减少到极少数边缘情况。

我必须注释掉以下标签 &lt;!--&lt;IntervalReadings&gt;--&gt; 以使您的 XML 格式正确。

XML Header 片段有一个默认命名空间:

  • xmlns="http://iec.ch/TC57/2011/schema/message"

XML Payload 片段有自己的两个额外命名空间:

  • xmlns:MeterReadings="http://iec.ch/TC57/2011/MeterReadings#"
  • xmlns="http://iec.ch/TC57/2011/MeterReadings#"

应考虑命名空间。

在下面查看。

SQL

DECLARE @tbl TABLE (ID INT IDENTITY PRIMARY KEY, xml_data XML);
INSERT INTO @tbl (xml_data) VALUES
(N'<RequestMessage xmlns="http://iec.ch/TC57/2011/schema/message"
                xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                xsi:noNamespaceSchemaLocation="Message.xsd">
    <Header>
        <Verb>created</Verb>
        <Noun>MeterReadings</Noun>
        <Timestamp>2021-03-08T00:57:18+01:00</Timestamp>
        <Source>Ipsum Lorum</Source>
        <AsyncReplyFlag>true</AsyncReplyFlag>
        <AckRequired>true</AckRequired>
        <MessageID>Ipsum Lorum</MessageID>
        <CorrelationID/>
    </Header>
    <Payload>
        <MeterReadings xmlns:MeterReadings="http://iec.ch/TC57/2011/MeterReadings#"
                       xmlns="http://iec.ch/TC57/2011/MeterReadings#">
            <MeterReading>
                <IntervalBlocks>
                    <IntervalReadings>
                        <timeStamp>2021-03-07T01:00:00+01:00</timeStamp>
                        <value>480.196</value>
                        <ReadingQualities>
                            <ReadingQualityType ref="3.0.0"/>
                        </ReadingQualities>
                    </IntervalReadings>
                    <!--<IntervalReadings>-->
                    <ReadingType ref="11.0.7.3.1.2.12.1.1.0.0.0.0.101.0.3.72.0"/>
                </IntervalBlocks>
                <Meter>
                    <mRID>0000000000000</mRID>
                    <status>
                        <remark>Ipsum Lorum</remark>
                        <value>ESP</value>
                    </status>
                </Meter>
                <UsagePoint>
                    <mRID>73599900000000</mRID>
                </UsagePoint>
            </MeterReading>
        </MeterReadings>
    </Payload>
</RequestMessage>');
-- DDL and sample data population, end

WITH XMLNAMESPACES(DEFAULT 'http://iec.ch/TC57/2011/schema/message')
SELECT id
    , c.value('(Noun/text())[1]','VARCHAR(30)') AS Noun
    , c.value('(Timestamp/text())[1]','DATETIMEOFFSET(0)') AS [timestamp]
FROM @tbl
    CROSS APPLY xml_data.nodes('/RequestMessage/Header') AS t(c);

输出

+----+---------------+----------------------------+
| id |     Noun      |         timestamp          |
+----+---------------+----------------------------+
|  1 | MeterReadings | 2021-03-08 00:57:18 +01:00 |
+----+---------------+----------------------------+

【讨论】:

  • 非常感谢。你的解决方案奏效了。
【解决方案2】:

您需要在 XQuery 的 XML 文档中尊重并包含​​ XML 命名空间

<RequestMessage xmlns="http://iec.ch/TC57/2011/schema/message"
                **********************************************

试试这样的:

WITH XMLNAMESPACES(DEFAULT N'http://iec.ch/TC57/2011/schema/message')
SELECT
    t.id,
    c.value('(Timestamp)[1]','varchar(max)') as timestamp
FROM
    load.t t
CROSS APPLY
    t.xml_data.nodes('RequestMessage/Header') AS m(c)

另外,当我尝试在我的 SQL Server 上运行它时,我收到一个错误,显示的 XML 格式不正确.....

更新:

如果您还需要访问 Payload 部分中的位 - 您还需要尊重那里的 那个 XML 命名空间:

<MeterReadings xmlns:MeterReadings="http://iec.ch/TC57/2011/MeterReadings#"
               xmlns="http://iec.ch/TC57/2011/MeterReadings#">
               ***********************************************

试试这个:

WITH XMLNAMESPACES(N'http://iec.ch/TC57/2011/schema/message' as hdr,
                   N'http://iec.ch/TC57/2011/MeterReadings#' as mr)
SELECT
    t.id,
    c.value('(hdr:Timestamp)[1]', 'varchar(50)') AS timestamp,
    col.value('(mr:MeterReading/mr:IntervalBlocks/mr:IntervalReadings/mr:timeStamp)[1]', 'varchar(50)') AS MeterReadingsTimestamp
FROM
    load.t t
CROSS APPLY
    t.xml_data.nodes('/hdr:RequestMessage/hdr:Header') AS m(c)
CROSS APPLY
    t.xml_data.nodes('/hdr:RequestMessage/hdr:Payload/mr:MeterReadings') AS mr(col)

【讨论】:

  • 非常感谢。你的解决方案奏效了。
  • 为什么它适用于 Header 但是当我为 Payload 运行它(更改命名空间)时它不起作用? WITH XMLNAMESPACES(DEFAULT N'http://iec.ch/TC57/2011/MeterReadings#') SELECT t.file_name,c.value('(/UsagePoint/mRID)[1]','varchar(max)') as timestamp FROM load.t t OUTER APPLY t.xml_data.nodes('RequestMessage/Payload') AS m(c) Yitzak 提到 Payload 有 2 个命名空间。这可能是问题吗?是否需要以某种方式将两个命名空间都加载到 WITH cte?
  • @MattiasW:再一次 - 你有一个 XML 命名空间,但你没有将它集成到你的 XQuery 中!用如何执行此操作的示例更新了我的回复。
  • 现在我看到了你是如何使用命名空间的。它似乎绑定到别名,因此它将“hdr:”绑定到“iec.ch/TC57/2011/schema/message”,将“mr:”绑定到iec.ch/TC57/2011/MeterReadings#
  • @MattiasW: 是的 - 确切地说 - 命名空间实际上是“iec.ch/TC57/2011/MeterReadings#”部分 - 它的 前缀 用于在 XPath 表达式中引用它跨度>
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-08
  • 2020-08-07
  • 2012-04-07
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多