Bagaimana cara membaca kode HTML sebagai XML dan mendapatkan output seperti sampel dalam sql?

11

Saya memiliki kode HTML yang disimpan dalam basis data, dan saya ingin membacanya sebagai XML.

Kode saya:

http://rextester.com/RMEHO89992

Ini adalah contoh kode HTML yang saya miliki:

<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>

dan ini adalah contoh dari output yang saya butuhkan:

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

Saya perlu mendapatkan nilai di dalam <h4>tag sebagai Category, <span>tag pertama sebagai Seleksi, dan sisanya dari nilai sebagai string gabungan.

Saya sudah mencoba pertanyaan berikut:

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) 

dan :

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)

Tetapi hanya mendapatkan kategori pertama, dan tidak mendapatkan semua nilai togheter.

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 

Mungkin ada kategori N, dan nilainya mungkin atau mungkin tidak ada di dalam <span>tag. Bagaimana saya bisa mendapatkan semua kategori dengan nilainya yang sesuai? atau dapatkan:

category              h4 number
--------            -----------
 A                     1
 B                     2
  • 1, rata-rata = h4 pertama, 2, rata-rata = h4 detik
 ul number         Selection        Value                    
    ---------        ---------        ------------             
    1                Ab               AD                  
    1                Ag               AL                       
    2                Bb               BD                   
    2                Bg               BL       

hubungan antara nomor ul kolom dan nomor h4. saya tidak bisa.

Pasukan Merah
sumber
1
Apakah Anda yakin hasil yang diharapkan benar? Bukankah seharusnya AD ACuntuk baris pertama di kolom ketiga?
Mikael Eriksson
Saya mencoba membangun komunikasi antara node` h4` dan `ul`.
RedArmy

Jawaban:

7

Ini tidak sepenuhnya elegan tetapi tampaknya melakukan pekerjaan itu.

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

Yang kembali

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

Saya berasumsi ini adalah apa yang Anda inginkan karena tabel hasil yang diinginkan dalam pertanyaan tidak mengembalikan "sisa nilai sebagai string gabungan"

Martin Smith
sumber
14

Saya mencoba menjalin komunikasi antara node h4dan ul.

Anda dapat menggunakan <<dan >>operator untuk memeriksa apakah suatu simpul adalah sebelum atau sesudah simpul lain dalam urutan dokumen. Gabungkan bahwa dengan predikat pada posisi [1],, untuk mendapatkan kejadian pertama juga dalam urutan dokumen.

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:

<<dan >>disebut Operator Perbandingan Node Order

Jika Anda memiliki fragmen XML seperti ini:

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

Anda bisa mendapatkan semua node sebelum kemunculan pertama N3dengan kueri ini:

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

Hasil:

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

/*akan memberi Anda semua root node. Yang terlampir []adalah predikat. .adalah simpul saat ini dan /N3[1]merupakan simpul N3 pertama dalam urutan dokumen di tingkat akar. Jadi dari setiap simpul root Anda mendapatkan node yang mendahului N3.

Berikut ini hampir sama permintaannya, hanya Anda yang mendapatkan simpul yang mengikutiN3 simpul pertama :

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

Untuk hanya mendapatkan simpul pertama setelah N3simpul pertama , Anda menambahkan predikat [1]:

select @X.query('/*[. >> /N3[1]][1]');
<N4>4</N4>
Mikael Eriksson
sumber