Как прочитать HTML-код как XML и получить вывод, как образец в SQL?

11

У меня есть HTML-код, хранящийся в базе данных, и я хочу читать его как XML.

Мои коды:

http://rextester.com/RMEHO89992

Это пример кода HTML, который у меня есть:

<div>
  <section>
       <h4>
         <span> A </span>
        </h4>
        <ul>
           <li>
              <span> Ab</span>
                     AD
              <span> AC </span>
           </li>
           <li>
              <span> Ag</span>
              <span> AL </span>
           </li>
        </ul>
        <h4>
              <span> B </span>
       </h4>
       <ul>
           <li>
              <span> Bb</span>
                     BD
              <span> BC </span>
           </li>
           <li>
              <span> Bg</span>
              <span> BL </span>
           </li>
        </ul>
   </section>
</div>

и это пример вывода мне нужно:

Category         Selection        Value                    
---------        ---------        ------------             
A                Ab               AD                  
A                Ag               AL                       
B                Bb               BD                   
B                Bg               BL                       

Мне нужно получить значение внутри <h4>тега как a Category, первый <span>тег как Selection, а остальные значения как объединенную строку.

Я пробовал следующий запрос:

SELECT 
    (  isnull(t.v.value('(h4/span/span[1]/text())[1]','nvarchar(max)'),'') 
     + isnull(t.v.value('(h4/span/text())[1]','nvarchar(max)'),'')
     + isnull(t.v.value('(h4/span/span[2]/text())[2]','nvarchar(max)'),'')
    ) AS [Category],
    (  isnull(c.g.value('(span[1]/text())[1]','nvarchar(max)'),'')
     + isnull(c.g.value('(span[1]/span/text())[1]','nvarchar(max)'),'')
     + isnull(c.g.value('(span[1]/text())[2]','nvarchar(max)'),'')
    ) AS [Selection],
    (  isnull(c.g.value('(span[2]/text())[1]','nvarchar(max)'),'')
     + isnull(c.g.value('(span[2]/span/text())[1]','nvarchar(max)'),'')
     + isnull(c.g.value('(span[2]/text())[2]','nvarchar(max)'),'')
    ) AS [Value]
FROM @htmlXML.nodes('div/section') as t(v)
CROSS APPLY t.v.nodes('./ul/li') AS c(g) 

и :

SELECT 
       t.v.value('.','nvarchar(max)')
       ,
     --( isnull(t.v.value('(h4/span/span[1]/text())[1]','nvarchar(max)'),'')+isnull(t.v.value('(h4/span/text())[1]','nvarchar(max)'),'')+isnull(t.v.value('(h4/span/span[2]/text())[2]','nvarchar(max)'),''))AS [Category],

          ( isnull(c.g.value('(span[1]/text())[1]','nvarchar(max)'),'')+isnull(c.g.value('(span[1]/span/text())[1]','nvarchar(max)'),'')+isnull(c.g.value('(span[1]/text())[2]','nvarchar(max)'),''))AS [Selection]

           ,
         ( isnull(c.g.value('(span[2]/text())[1]','nvarchar(max)'),'')+isnull(c.g.value('(span[2]/span/text())[1]','nvarchar(max)'),'')+isnull(c.g.value('(span[2]/text())[2]','nvarchar(max)'),''))AS [Value]
    FROM    @htmlXML.nodes('div/section/h4/span') as t(v)
    CROSS APPLY @htmlXML.nodes('div/section/ul/li') AS c(g)

Но он получает только первую категорию и не получает все значения вместе.

Category         Selection        Value
---------        ---------        ------------
A                Ab               AC 
B                Ab               AC 
A                Ag               AL
B                Ag               AL 
A                Bb               BC
B                Bb               BC 
A                Bg               BL 
B                Bg               BL 

Может быть N категорий, и значения могут быть или не быть внутри <span>тегов. Как я могу получить все категории с соответствующими значениями? или получите:

category              h4 number
--------            -----------
 A                     1
 B                     2
  • 1, среднее значение = h4 сначала, 2, среднее значение = h4 секунды
 ul number         Selection        Value                    
    ---------        ---------        ------------             
    1                Ab               AD                  
    1                Ag               AL                       
    2                Bb               BD                   
    2                Bg               BL       

связь между номером столбца ul и номером h4. я не могу.

Красная армия
источник
1
Вы уверены, что ожидаемый результат правильный? Разве это не должно быть AD ACдля первого ряда в третьем столбце?
Микаэль Эрикссон
Я пытаюсь установить связь между узлами `h4` и` ul`.
RedArmy

Ответы:

7

Это не совсем элегантно, но, кажется, делает работу.

DECLARE @X XML = REPLACE(REPLACE(@S, '<h4>', '<foo><h4>'), '</ul>', '</ul></foo>')

SELECT Category = x.value('../../h4[1]/span[1]', 'varchar(10)'),
       Selection = x.value('descendant-or-self::text()[1]', 'varchar(10)'),
       Value = REPLACE(
                REPLACE(
                 REPLACE(
                  LTRIM(
                   RTRIM(
                    REPLACE(
                     REPLACE(
                      CAST(x.x.query('fn:data(descendant-or-self::text()[fn:position() > 1])') AS VARCHAR(MAX))
                     , char(10), '')
                    , char(13), '')
                   )
                  )
                 , '  ', ' |')
                , '| ', '')
               , '|', '')
FROM   @X.nodes('div/section/foo/ul/li') x(x)
ORDER  BY Category,
          Selection

Который возвращается

+----------+-----------+-------+
| Category | Selection | Value |
+----------+-----------+-------+
|  A       |  Ab       | AD AC |
|  A       |  Ag       | AL    |
|  B       |  Bb       | BD BC |
|  B       |  Bg       | BL    |
+----------+-----------+-------+

Я предполагаю, что это то, что вы хотите, так как таблица желаемых результатов в вопросе не возвращает «остальные значения в виде объединенной строки»

Мартин Смит
источник
14

Я пытаюсь установить связь между узлами h4и ul.

Вы можете использовать оператор <<and, >>чтобы проверить, находится ли узел до или после другого узла в порядке документов. Объедините это с предикатом позиции, [1]чтобы получить первое вхождение также в порядке документа.

select H4.X.value('(span/text())[1]', 'varchar(10)') as Section,
       UL.X.query('.') as UL
from @X.nodes('/div/section/h4') as H4(X)
  cross apply H4.X.nodes('(let $h4 := . (: Save current h4 node :)
                           return /div/section/ul[$h4 << .])[1]') as UL(X);

rextester:

<<и >>называются операторами сравнения порядка узлов

Если у вас есть такой фрагмент XML:

<N1>1</N1>
<N2>2</N2>
<N3>3</N3>
<N4>4</N4>
<N5>5</N5>

вы можете получить все узлы до первого появления N3этого запроса:

select @X.query('/*[. << /N3[1]]');

Результат:

<N1>1</N1>
<N2>2</N2>

/*даст вам все корневые узлы. Что заключено в []это предикат. .является текущим узлом и /N3[1]является первым узлом N3 в порядке документа на корневом уровне. Таким образом, от каждого корневого узла вы получаете узлы, которые предшествуют N3.

Здесь почти тот же запрос, только вы получаете узлы, которые следуют за первым N3узлом:

select @X.query('/*[. >> /N3[1]]');
<N4>4</N4>
<N5>5</N5>

Чтобы получить первый узел только после первого N3, вы добавляете предикат [1]:

select @X.query('/*[. >> /N3[1]][1]');
<N4>4</N4>
Микаэль Эрикссон
источник