Tag Archives: XPath

XPath & libxml2

XML은 꽤 오래전부터 사용했지만, 여러가지 복잡한 용어들이 나오면서 좀 멀어졌던 느낌이었는데, XPath는 정말 프로그래머에게 유용한 툴인것 같네요.

XPath는 XML 문서에서 쉽게 element를 찾는 API로 쿼리를 문자열로 넘기면 조건에 맞는 element나 element 리스트를 반환하게 됩니다. 1.0 버전이 있고 2.0 버전이 최근에 나왔습니다. 아직까지는 라이브러리들이 1.0 기반이 대부분입니다.

쿼리는 예제로 살펴보는것이 빠른듯하네요.

“A/B/C” : A element 밑에 B element 밑에 C element들은 찾을때
“/A/B/C” : 위와 같지만 A가 최상위 element.
“/A/B/C[1]” : C element중 첫번째
“/A/B/C[2]” : C element중 두번째
“//C” : 모든 C element
“B//C” : B 하위에 있는 C element
“A/B/*” : A element 밑에 B element 바로 밑의 모든 element
“A/B//*” : A element 밑에 B element 밑의 모든 element (하위 element 포함)
“//*” : 문서의 모든 element

libxml2라는 C 라이브러리가 있지만 python wrapper를 이용하여 python에서 위의 쿼리들을 돌려봤습니다. 테스트해본 결과 /로 시작하지 않는 쿼리들은 제대로 동작하지 않더군요. (검색되는 결과가 없음) 이런 쿼리들은 앞에 //를 붙여주면 제대로 동작합니다.

아래는 python 소스입니다. 중간에 예제 XML을 보기 좋게(?) 들여쓰기했놨지만 출력할때 한줄로 볼수 있도록 xml에서 공백과 newline을 제거합니다.

[CODE type=python]
import libxml2

def xpathElements(ctxt, query):
   if query[0] == ‘/':
       print “\”%s\”” % query
   else:
       print “\”%s\” -> \”//%s\”” % (query, query)
       query = “//” + query
   res = ctxt.xpathEval(query)
   for e in res:
       print ”    %s (%s)” % (e.name, e)

xml = “””
<A>
   <B>
       <C id=’c1’/>
       <C id=’c2′>
           <D/>
       </C>
       <E>
           <F/>
           <A>
               <B>
                   <C/>
               </B>
           </A>
       </E>
   </B>
</A>”””

xml = ”.join([l.strip() for l in xml.splitlines()])

doc = libxml2.parseDoc(xml)

ctxt = doc.xpathNewContext()

xpathElements(ctxt, “A/B/C”)
xpathElements(ctxt, “/A/B/C”)
xpathElements(ctxt, “/A/B/C[1]”)
xpathElements(ctxt, “/A/B/C[2]”)
xpathElements(ctxt, “/A/B/C[3]”)
xpathElements(ctxt, “//C”)
xpathElements(ctxt, “//B//C”)
xpathElements(ctxt, “A/B/*”)
xpathElements(ctxt, “A/B//*”)
xpathElements(ctxt, “//*”)
[/HTML][/CODE]

다음은 실행결과입니다.

“A/B/C” -> “//A/B/C”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   C (<C/>)
“/A/B/C”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
“/A/B/C[1]”
   C (<C id=”c1″/>)
“/A/B/C[2]”
   C (<C id=”c2″><D/></C>)
“/A/B/C[3]”
“//C”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   C (<C/>)
“//B//C”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   C (<C/>)
“A/B/*” -> “//A/B/*”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   E (<E><F/><A><B><C/></B></A></E>)
   C (<C/>)
“A/B//*” -> “//A/B//*”
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   D (<D/>)
   E (<E><F/><A><B><C/></B></A></E>)
   F (<F/>)
   A (<A><B><C/></B></A>)
   B (<B><C/></B>)
   C (<C/>)
“//*”
   A (<A><B><C id=”c1″/><C id=”c2″><D/></C><E><F/><A><B><C/></B></A></E></B></A>)
   B (<B><C id=”c1″/><C id=”c2″><D/></C><E><F/><A><B><C/></B></A></E></B>)
   C (<C id=”c1″/>)
   C (<C id=”c2″><D/></C>)
   D (<D/>)
   E (<E><F/><A><B><C/></B></A></E>)
   F (<F/>)
   A (<A><B><C/></B></A>)
   B (<B><C/></B>)
   C (<C/>)