Jak sparsować XML w oparciu o nextsibling

0

Witajcie.

Potrzebuję sparsować xml przy użyciu xpath. Xml z grubsza wygląda tak:

<RESULT>
       <INFO_NAME>text1</INFO_NAME>
       <INFO_VALUE>500</INFO_VALUE>
       <INFO_NAME>text2</INFO_NAME>
       <INFO_VALUE>12</INFO_VALUE>
       <INFO_NAME>text3</INFO_NAME>
       <INFO_VALUE>400</INFO_VALUE>
       <INFO_NAME>text4</INFO_NAME>
       <INFO_VALUE>60</INFO_VALUE>
   </RESULT>

Poradziłem sobie z tym w następujący sposób:

select unnest(xpath('INFO_NAME/text()',x.node))::text infoname,
       unnest(xpath('INFO_VALUE/text()',x.node))::text infovalue
  from (SELECT unnest(xpath('//RESULT', axml)) node

Wszystko fajnie zadziałało jeśli ilość NAME pokrywa się z ilością VALUE. W przypadku gdy NAME mam np 4, a VALUE mam tylo 3 węzły to wtedy robi się kartezjan ... w pythonie jest taka fajna funkcja o nazwie nextsibling, która pobiera kolejny węzeł. Niestety w postgresql nie znalazłem czegoś takiego. Macie pomysł jak to ugryźć?

Dla ułatwienia poprawny:
http://sqlfiddle.com/#!17/047a1/4
i niepoprawny
http://sqlfiddle.com/#!17/047a1/5

0

@woolfik:

<RESULT>
       <INFO_NAME>text1</INFO_NAME>
       <INFO_NAME>text2</INFO_NAME>
       <INFO_NAME>text3</INFO_NAME>
       <INFO_VALUE>60</INFO_VALUE>
   </RESULT>

Oczekiwany rezultat:

text1, null
text2, null
test3, 60 

Tak?

  1. Czy ten XML może być bardziej zepsuty?, np.
    <RESULT>
    <INFO_VALUE>60</INFO_VALUE>
    <INFO_VALUE>70</INFO_VALUE>
    </RESULT>

  2. Masz możliwość wpłynięcia na strukturę tego XMLa u źródła? Jest mega słaba.

1

Połowiczne rozwiazanie, nie weźmie INFO_NAME jeżeli następny węzeł to nie INFO_VALUE


select unnest(xpath('INFO_NAME[following-sibling::*[1][name()=''INFO_VALUE'']]/text()',x))::text infoname,
       unnest(xpath('INFO_VALUE/text()',x))::text infovalue
  from txml x

rozkraczy się jak będzie odwrotna sytuacja, znaczy value bez name

http://sqlfiddle.com/#!17/047a1/113

UPDATE:

własciwie mozna odrzucić value bez name i wtedy zadziała dobrze w obie strony:

select unnest(xpath('INFO_NAME[following-sibling::*[1][name()=''INFO_VALUE'']]/text()',x))::text infoname,
       unnest(xpath('INFO_VALUE[preceding-sibling::*[1][name()=''INFO_NAME'']]/text()',x))::text infovalue
  from txml x

http://sqlfiddle.com/#!17/047a1/116

1

coś takiego może być?

with txml(x) as(
 values 
/*
 ('<RESULT>
       <INFO_NAME>text1</INFO_NAME>
       <INFO_VALUE>500</INFO_VALUE>
       <INFO_NAME>text2</INFO_NAME>
       <INFO_VALUE>12</INFO_VALUE>
       <INFO_NAME>text3</INFO_NAME>
       <INFO_VALUE>400</INFO_VALUE>
       <INFO_NAME>text4</INFO_NAME>
       <INFO_VALUE>60</INFO_VALUE>
   </RESULT>'::xml)
 */
 ('<RESULT>
       <INFO_NAME>text1</INFO_NAME>
       <INFO_VALUE>500</INFO_VALUE>
       <INFO_NAME>text2</INFO_NAME>
       <INFO_VALUE>12</INFO_VALUE>
       <INFO_NAME>text3</INFO_NAME>
       <INFO_VALUE>400</INFO_VALUE>
       <INFO_NAME>text4</INFO_NAME>
   </RESULT>'::xml)
 )
, infonames as 
(   
select row_number() over() lp, infoname from (select unnest(xpath('INFO_NAME/text()',x.node))::text infoname from (SELECT unnest(xpath('//RESULT', s.x)) node from txml s) x) x  
)
, infovalues as
(
select row_number() over() lp, infovalue from (select unnest(xpath('INFO_VALUE/text()',x.node))::text infovalue from (SELECT unnest(xpath('//RESULT', s.x)) node from txml s) x) x  
)

select * from infonames full join infovalues using(lp)
       
		  ```

1 użytkowników online, w tym zalogowanych: 0, gości: 1