Comment lire du code HTML en XML et obtenir la sortie comme l'exemple en sql?

11

J'ai du code HTML stocké dans la base de données et je veux le lire en XML.

Mes codes:

http://rextester.com/RMEHO89992

Voici un exemple du code HTML que j'ai:

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

et ceci est un exemple de la sortie dont j'ai besoin:

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

J'ai besoin d'obtenir la valeur à l'intérieur de la <h4>balise en tant que Category, la première <span>balise en tant que sélection et le reste des valeurs en tant que chaîne concaténée.

J'ai essayé la requête suivante:

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) 

et :

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)

Mais il n'obtient que la première catégorie et n'obtient pas toutes les valeurs ensemble.

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 

Il peut y avoir N catégories et les valeurs peuvent ou non être à l'intérieur des <span>balises. Comment puis-je obtenir toutes les catégories avec leur valeur correspondante? ou obtenez:

category              h4 number
--------            -----------
 A                     1
 B                     2
  • 1, moyenne = h4 en premier, 2, moyenne = h4 en second
 ul number         Selection        Value                    
    ---------        ---------        ------------             
    1                Ab               AD                  
    1                Ag               AL                       
    2                Bb               BD                   
    2                Bg               BL       

relation entre le numéro ul de la colonne et le numéro h4. Je ne peux pas.

Armée rouge
la source
1
Êtes-vous sûr que le résultat attendu est correct? Cela ne devrait-il pas être AD ACpour la première ligne de la troisième colonne?
Mikael Eriksson
J'essaie d'établir une communication entre les nœuds «h4» et «ul».
RedArmy

Réponses:

7

Ce n'est pas exactement élégant mais semble faire l'affaire.

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

Qui revient

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

Je suppose que c'est ce que vous voulez car le tableau de résultats souhaité dans la question ne renvoie pas le "reste des valeurs sous forme de chaîne concaténée"

Martin Smith
la source
14

J'essaie d'établir une communication entre les nœuds h4et ul.

Vous pouvez utiliser l' opérateur <<et >>pour vérifier si un nœud est avant ou après un autre nœud dans l'ordre des documents. Combinez cela avec un prédicat sur la position, [1]pour obtenir la première occurrence également dans l'ordre du document.

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:

<<et >>sont appelés Opérateurs de comparaison d'ordre des nœuds

Si vous avez un fragment XML comme celui-ci:

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

vous pouvez obtenir tous les nœuds avant la première occurrence de N3avec cette requête:

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

Résultat:

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

/*vous donnera tous les nœuds racine. Ce qui est enfermé []est un prédicat. .est le nœud actuel et /N3[1]est le premier nœud N3 dans l'ordre des documents au niveau racine. Donc, à partir de chaque nœud racine, vous obtenez les nœuds qui précèdent N3.

Voici presque la même requête, seulement vous obtenez les nœuds qui suivent le premier N3nœud:

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

Pour obtenir uniquement le premier nœud après le premier N3nœud, vous ajoutez le prédicat [1]:

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