콘텐츠로 이동

Converter - XPath 가이드

XPath (XML Path Language)는 StAX-XML Converter에서 요소를 선택하는 주요 방법입니다. 이 가이드는 지원되는 모든 XPath 패턴과 모범 사례를 다룹니다.

XPath는 XML 문서에서 노드를 선택하는 쿼리 언어입니다. XML용 CSS 선택자와 같습니다:

  • CSS 선택자: div.container > p
  • XPath: /div[@class='container']/p

Converter는 XPath를 사용하여 XML에서 어떤 요소를 추출할지 지정합니다.

/로 문서 루트에서 시작:

// <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()') // "안녕 " (직접 텍스트만)

조건절은 [...]를 사용하여 조건에 따라 요소를 필터링합니다.

// 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() 함수는 스트리밍 파서 제약으로 인해 지원되지 않습니다 (전체 문서 버퍼링 필요).

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'
)
});
// ❌ 너무 광범위 - 모든 name과 일치
x.string().xpath('//name')
// ✅ 구체적인 경로
x.string().xpath('/user/profile/name')
// ✅ 조건절 사용
x.string().xpath('//user[@role="admin"]/name')
// ❌ 반복적인 절대 경로
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');
// ❌ 모든 제품을 가져와 코드에서 필터
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')
패턴예제설명
/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상대 속성

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()을 사용하여 데이터를 처리하세요.