Converter - XPath 가이드
XPath (XML Path Language)는 StAX-XML Converter에서 요소를 선택하는 주요 방법입니다. 이 가이드는 지원되는 모든 XPath 패턴과 모범 사례를 다룹니다.
XPath란?
섹션 제목: “XPath란?”XPath는 XML 문서에서 노드를 선택하는 쿼리 언어입니다. XML용 CSS 선택자와 같습니다:
- CSS 선택자:
div.container > p - XPath:
/div[@class='container']/p
Converter는 XPath를 사용하여 XML에서 어떤 요소를 추출할지 지정합니다.
XPath 기초
섹션 제목: “XPath 기초”절대 경로
섹션 제목: “절대 경로”/로 문서 루트에서 시작:
// <book> 바로 아래의 <title> 선택x.string().xpath('/book/title')
// 중첩 경로x.string().xpath('/library/book/title')하위 요소 검색
섹션 제목: “하위 요소 검색”//를 사용하여 문서 어디서나 검색:
// 어떤 깊이에서든 <title> 찾기x.string().xpath('//title')
// 알 수 없는 구조에 유용x.array(x.string(), '//error') // 모든 에러 메시지⚠️ 성능 참고: //는 전체 문서를 검색합니다. 가능하면 절대 경로를 사용하세요.
⚠️ 중요 제한: 하나의 경로에 여러 // 연산자는 지원되지 않습니다.
// ❌ 잘못됨 - 파서 오류 발생x.string().xpath('//root//books')x.array(x.string(), '//section//item')
// ✅ 올바른 대안x.string().xpath('//books') // 단일 하위 요소x.string().xpath('/root//name') // 절대 + 하위x.array(x.string(), '//item') // 단일 하위 요소기술적 이유: DOM 없는 이벤트 기반 스트리밍 파서입니다. 여러 //는 중첩된 전체 문서 스캔이 필요하여 무한 루프(//node//node)를 유발합니다.
상대 경로
섹션 제목: “상대 경로”./를 현재 컨텍스트에 상대적인 경로로 사용:
const specs = x.object({ cpu: x.string().xpath('./cpu'), ram: x.string().xpath('./ram'), storage: x.string().xpath('./storage')}).xpath('/product/specs');속성 선택
섹션 제목: “속성 선택”단순 속성
섹션 제목: “단순 속성”/@를 사용하여 속성 값 선택:
// id 속성 선택x.string().xpath('/book/@id')
// 중첩 속성x.number().xpath('/product/item/@price')하위 속성
섹션 제목: “하위 속성”//@로 어디서나 속성 검색:
// 모든 href 속성 찾기x.string().xpath('//@href')
// 모든 id 속성x.array(x.string(), '//@id')텍스트 콘텐츠 선택
섹션 제목: “텍스트 콘텐츠 선택”text()를 사용하여 직접 텍스트 콘텐츠만 선택:
// 요소 콘텐츠 (중첩 요소 포함)x.string().xpath('/message')
// 직접 텍스트 콘텐츠만 (중첩 요소 제외)x.string().xpath('/message/text()')
// 차이점 예제:// XML: <div>안녕 <span>세계</span></div>x.string().xpath('/div') // "안녕 세계" (모든 텍스트)x.string().xpath('/div/text()') // "안녕 " (직접 텍스트만)XPath 조건절
섹션 제목: “XPath 조건절”조건절은 [...]를 사용하여 조건에 따라 요소를 필터링합니다.
속성 값 조건절
섹션 제목: “속성 값 조건절”// category="fiction"인 책const fictionBooks = x.array( x.object({ title: x.string().xpath('./title'), author: x.string().xpath('./author') }), '//book[@category="fiction"]');다중 조건
섹션 제목: “다중 조건”// 사용 가능하고 재고가 있는 제품x.array( x.object({...}), '//product[@available="true"][@inStock="true"]');위치 조건절
섹션 제목: “위치 조건절”// 첫 번째 책x.object({...}).xpath('//book[1]')
// 두 번째 책x.object({...}).xpath('//book[2]')
// 세 번째 책x.object({...}).xpath('//book[3]')⚠️ 위치 제한: [1], [2]와 같은 숫자 위치만 지원됩니다. last() 및 position() 함수는 스트리밍 파서 제약으로 인해 지원되지 않습니다 (전체 문서 버퍼링 필요).
실전 XPath 예제
섹션 제목: “실전 XPath 예제”RSS 피드 파싱
섹션 제목: “RSS 피드 파싱”const rss = x.object({ channelTitle: x.string().xpath('/rss/channel/title'), channelLink: x.string().xpath('/rss/channel/link'), items: x.array( x.object({ title: x.string().xpath('./title'), link: x.string().xpath('./link'), description: x.string().xpath('./description'), pubDate: x.string().xpath('./pubDate') }), '/rss/channel/item' )});전자상거래 제품 카탈로그
섹션 제목: “전자상거래 제품 카탈로그”const catalog = x.object({ storeName: x.string().xpath('/catalog/@name'), categories: x.array( x.object({ id: x.number().xpath('./@id'), name: x.string().xpath('./@name'), products: x.array( x.object({ id: x.number().xpath('./@id'), name: x.string().xpath('./name'), price: x.number().xpath('./price').min(0), inStock: x.string().xpath('./@inStock').transform(v => v === 'true'), tags: x.array(x.string(), './tag') }), './product' ) }), '/catalog/category' )});XPath 모범 사례
섹션 제목: “XPath 모범 사례”1. 구체적으로 작성
섹션 제목: “1. 구체적으로 작성”// ❌ 너무 광범위 - 모든 name과 일치x.string().xpath('//name')
// ✅ 구체적인 경로x.string().xpath('/user/profile/name')
// ✅ 조건절 사용x.string().xpath('//user[@role="admin"]/name')2. 객체와 함께 상대 경로 사용
섹션 제목: “2. 객체와 함께 상대 경로 사용”// ❌ 반복적인 절대 경로const user = x.object({ name: x.string().xpath('/user/profile/details/name'), email: x.string().xpath('/user/profile/details/email'), phone: x.string().xpath('/user/profile/details/phone')});
// ✅ 객체 XPath로 더 깔끔하게const user = x.object({ name: x.string().xpath('./name'), email: x.string().xpath('./email'), phone: x.string().xpath('./phone')}).xpath('/user/profile/details');3. 필터링에 조건절 사용
섹션 제목: “3. 필터링에 조건절 사용”// ❌ 모든 제품을 가져와 코드에서 필터const products = x.array(x.object({...}), '//product');const available = products.filter(p => p.available);
// ✅ XPath로 필터링const available = x.array( x.object({...}), '//product[@available="true"]');4. 하위 요소보다 절대 경로 선호
섹션 제목: “4. 하위 요소보다 절대 경로 선호”// ❌ 느림 - 전체 문서 검색x.string().xpath('//deeply/nested/element')
// ✅ 빠름 - 직접 경로x.string().xpath('/root/section/deeply/nested/element')일반 XPath 패턴 치트시트
섹션 제목: “일반 XPath 패턴 치트시트”| 패턴 | 예제 | 설명 |
|---|---|---|
/element | /book | 루트 요소 |
/parent/child | /book/title | 직접 자식 |
//element | //title | 문서 어디서나 |
/@attr | /@id | 현재 요소의 속성 |
//@attr | //@href | 어디서나 속성 |
/element/@attr | /book/@id | 특정 요소의 속성 |
//element[@attr="value"] | //book[@category="fiction"] | 속성 값이 있는 요소 |
//element[1] | //book[1] | 첫 번째 일치 요소 |
//element[2] | //book[2] | 두 번째 일치 요소 |
/element/text() | /message/text() | 직접 텍스트 콘텐츠만 |
./child | ./name | 상대 자식 |
./@attr | ./@id | 상대 속성 |
XPath 제한사항
섹션 제목: “XPath 제한사항”Converter는 XPath 1.0의 하위 집합을 지원합니다:
✅ 지원됨
섹션 제목: “✅ 지원됨”- 절대 경로:
/root/element - 하위 요소 검색:
//element - 속성:
/@attr,//@attr - 조건절:
[@attr="value"] - 숫자 위치:
[1],[2],[3](특정 위치만) - 텍스트 노드 함수:
text()(직접 텍스트 콘텐츠용) - 상대 경로:
./child
❌ 지원 안 됨
섹션 제목: “❌ 지원 안 됨”- 축:
following-sibling::,ancestor:: - 위치 함수:
last(),position()(스트리밍 파서 제약) - 문자열 함수:
contains(),starts-with(),substring() - 복잡한 표현식: 수학 연산
- 네임스페이스:
//ns:element - 다중 하위 연산자:
//parent//child(무한 루프 발생)
지원되지 않는 기능의 경우, 파싱 후 .transform()을 사용하여 데이터를 처리하세요.