T- SQL Parse XML Ответ в табличном формате - PullRequest
0 голосов
/ 21 марта 2020

Я борюсь с разбором ответа XML, который у меня есть. Мне нужно, чтобы значения заголовков были столбцами, а значения записей - данными внутри соответствующих строк. Ниже приведен пример возврата со значениями заголовка и 1 записью.

Где записи показывают, что xsi: nil = "true" будет равно NULL

<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
   <env:Header/>
   <env:Body>
      <ns2:getReportResultResponse xmlns:ns2="http://service.apiendpoint.com">
         <return>
            <header>
               <values>
                  <data>CUSTOMER NAME</data>
                  <data>DISPOSITION GROUP A</data>
                  <data>DISPOSITION GROUP B</data>
                  <data>DISPOSITION GROUP C</data>
                  <data>DISPOSITION PATH</data>
                  <data>FIRST DISPOSITION</data>
                  <data>LAST DISPOSITION</data>
                  <data>LIST NAME</data>
               </values>
            </header>
            <records>
               <values>
                  <data>Mark Smith</data>
                  <data>12</data>
                  <data>19</data>
                  <data>23</data>
                  <data xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
                   <data xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
                   <data xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
                  <data>Tier 1</data>
               </values>
            </records>
            </return>
      </ns2:getReportResultResponse>
   </env:Body>
</env:Envelope>

Ответы [ 3 ]

3 голосов
/ 21 марта 2020
declare @x xml = N'
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
   <env:Header/>
   <env:Body>
      <ns2:getReportResultResponse xmlns:ns2="http://service.apiendpoint.com">
         <return>
            <header>
               <values>
                  <data>CUSTOMER NAME</data>
                  <data>DISPOSITION GROUP A</data>
                  <data>DISPOSITION GROUP B</data>
                  <data>DISPOSITION GROUP C</data>
                  <data>DISPOSITION PATH</data>
                  <data>FIRST DISPOSITION</data>
                  <data>LAST DISPOSITION</data>
                  <data>LIST NAME</data>
               </values>
            </header>
            <records>
               <values>
                  <data>Mark Smith</data>
                  <data>12</data>
                  <data>19</data>
                  <data>23</data>
                  <data xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
                   <data xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
                   <data xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
                  <data>Tier 1</data>
               </values>
               <values>
                  <data>B</data>
                  <data>2</data>
                  <data>22</data>
                  <data>222</data>
                  <data xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
                   <data xsi:nil="false" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/><!-- ?? -->
                   <data xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
                   <data>Tier 2</data>
               </values>               
            </records>
            </return>
      </ns2:getReportResultResponse>
   </env:Body>
</env:Envelope>
';

select @x;

declare @sql nvarchar(max) = N'';

with xmlnamespaces ('http://schemas.xmlsoap.org/soap/envelope/' as env, 'http://service.apiendpoint.com' as ns2)
select 
    @sql = @sql + ',r.rec.value(''data[' + cast(colid as nvarchar(10)) + '][not(@xsi:nil="true")]'', ''nvarchar(500)'') as ' + colname
from 
(
    select 
        quotename(hd.h.value('.', 'sysname')) as colname,
        row_number() over(order by hd.h) as colid
    from @x.nodes('/env:Envelope/env:Body/ns2:getReportResultResponse/return/header/values/data') as hd(h)
) as src
order by colid;

select @sql = stuff(@sql, 1, 1, N'');

select @sql = N'with xmlnamespaces (''http://schemas.xmlsoap.org/soap/envelope/'' as env, ''http://service.apiendpoint.com'' as ns2, ''http://www.w3.org/2001/XMLSchema-instance'' as xsi)
select 
' + @sql + N'
from @x.nodes(''/env:Envelope/env:Body/ns2:getReportResultResponse/return/records/values'') as r(rec)
';

exec sp_executesql @stmt = @sql, @params = N'@x xml', @x = @x;
2 голосов
/ 23 марта 2020

Вот еще одно решение. Это очень близко к методу @ lptr.

Он использует XQuery и FLWOR выражение для создания динамического c SELECT предложения конечного SQL оператор.

SQL

DECLARE @x xml = N'
<env:Envelope xmlns:env="http://schemas.xmlsoap.org/soap/envelope/">
   <env:Header/>
   <env:Body>
      <ns2:getReportResultResponse xmlns:ns2="http://service.apiendpoint.com">
         <return>
            <header>
               <values>
                  <data>CUSTOMER NAME</data>
                  <data>DISPOSITION GROUP A</data>
                  <data>DISPOSITION GROUP B</data>
                  <data>DISPOSITION GROUP C</data>
                  <data>DISPOSITION PATH</data>
                  <data>FIRST DISPOSITION</data>
                  <data>LAST DISPOSITION</data>
                  <data>LIST NAME</data>
               </values>
            </header>
            <records>
               <values>
                  <data>Mark Smith</data>
                  <data>12</data>
                  <data>19</data>
                  <data>23</data>
                  <data xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
                   <data xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
                   <data xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
                  <data>Tier 1</data>
               </values>
               <values>
                  <data>B</data>
                  <data>2</data>
                  <data>22</data>
                  <data>222</data>
                  <data xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
                   <data xsi:nil="false" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/><!-- ?? -->
                   <data xsi:nil="true" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"/>
                   <data>Tier 2</data>
               </values>               
            </records>
            </return>
      </ns2:getReportResultResponse>
   </env:Body>
</env:Envelope>';

DECLARE @sql NVARCHAR(MAX) = N''
    , @separator CHAR(1) = ',';

WITH XMLNAMESPACES ('http://schemas.xmlsoap.org/soap/envelope/' as env, 'http://service.apiendpoint.com' as ns2)
SELECT @sql = @x.query('
    for $r in /env:Envelope/env:Body/ns2:getReportResultResponse/return/header/values/data
    let $pos := count(env:Envelope/env:Body/ns2:getReportResultResponse/return/header/values/data[. << $r]) + 1
    let $line := concat("c.value(''(data[", string($pos), "]/text())[1]'', ''VARCHAR(50)'') AS [", string(($r/text())[1]),"]")
    return if ($r is (/env:Envelope/env:Body/ns2:getReportResultResponse/return/header/values/data[last()])[1]) then string($line)
            else concat($line, sql:variable("@separator"))
').value('.', 'NVARCHAR(MAX)');

SET @sql = N';WITH XMLNAMESPACES (''http://schemas.xmlsoap.org/soap/envelope/'' as env, ''http://service.apiendpoint.com'' as ns2)
SELECT ' + @sql + N'
FROM @x.nodes(''/env:Envelope/env:Body/ns2:getReportResultResponse/return/records/values'') AS t(c)
';

EXEC sp_executesql @stmt = @sql, @params = N'@x xml', @x = @x;

Выход

+---------------+---------------------+---------------------+---------------------+------------------+-------------------+------------------+-----------+
| CUSTOMER NAME | DISPOSITION GROUP A | DISPOSITION GROUP B | DISPOSITION GROUP C | DISPOSITION PATH | FIRST DISPOSITION | LAST DISPOSITION | LIST NAME |
+---------------+---------------------+---------------------+---------------------+------------------+-------------------+------------------+-----------+
| Mark Smith    |                  12 |                  19 |                  23 | NULL             | NULL              | NULL             | Tier 1    |
| B             |                   2 |                  22 |                 222 | NULL             | NULL              | NULL             | Tier 2    |
+---------------+---------------------+---------------------+---------------------+------------------+-------------------+------------------+-----------+
2 голосов
/ 21 марта 2020

Если у вас есть данные XML в SQL серверной переменной @XmlData, вы можете использовать этот XQuery для получения имен столбцов ("заголовков"):

WITH XMLNAMESPACES ('http://schemas.xmlsoap.org/soap/envelope/' AS env, 'http://service.apiendpoint.com' AS ns2)
    SELECT
        XCol.value('(.)[1]', 'varchar(50)')
    FROM
        @XmlData.nodes('/env:Envelope/env:Body/ns2:getReportResultResponse/return/header/values/data') AS XHdr(XCol);

Это довольно просто, поскольку вы можете предположить, что каждый отдельный заголовок действительно является строкой (поэтому вы можете сделать вызов .value('(.)[1]', 'varchar(50)') и быть в безопасности).

Однако для данных - как @Serg уже упоминалось в комментарий - если вы не можете как-то узнать (или выяснить), каковы типы данных элементов данных, это будет сложнее ... Используя тот же подход - предполагая, что все является строкой - будет работа - но тогда вы можете потерять ценную информацию о ваших битах данных:

WITH XMLNAMESPACES ('http://schemas.xmlsoap.org/soap/envelope/' AS env, 'http://service.apiendpoint.com' AS ns2)
    SELECT
        XCol.value('(.)[1]', 'varchar(50)')
    FROM
        @XmlData.nodes('/env:Envelope/env:Body/ns2:getReportResultResponse/return/records/values/data') AS XData(XCol)
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...