просто о сложном


XPath за 10 минут

tags: xpath, xml

XPath описан в стандарте. Согласно написанному там "XPath is a language for addressing parts of an XML document ... ". По-русски можно сказать, что XPath нужен для разбора (parsing) XML документов. В приведенных ниже примерах рассматриваются наиболее часто употребимые (по мнению автора) конструкции языка.


Если путь начинается с символа /, то он представляет абсолютный путь к заданному элементу.

/AAA /AAA/DDD/EEE
<AAA>
	<BBB/>
	<CCC/>
	<BBB/>
	<DDD>
		<EEE/>
	</DDD>
	<CCC/>
</AAA>
<AAA>
	<BBB/>
	<CCC/>
	<BBB/>
	<DDD>
		<EEE/>
	</DDD>
	<CCC/>
</AAA>

Если путь начинается с //, то будут выбраны все элементы, соответствующие указанному шаблону.

//BBB //DDD/BBB
<AAA>
	<BBB/>
	<CCC/>
	<BBB/>
	<DDD>
		<BBB/>
	</DDD>
	<CCC>
		<DDD>
			<BBB/>
			<BBB/>
		</DDD>
	</CCC>
</AAA>
<AAA>
	<BBB/>
	<CCC/>
	<BBB/>
	<DDD>
		<BBB/>
	</DDD>
	<CCC>
		<DDD>
			<BBB/>
			<BBB/>
		</DDD>
	</CCC>
</AAA>

Символ * подразумевает любой узел в пути.

/AAA/CCC/DDD/* /*/*/*/BBB//*
<AAA>
	<XXX>
		<DDD>
			<BBB/>
			<BBB/>
			<EEE/>
			<FFF/>
		</DDD>
	</XXX>
	<CCC>
		<DDD>
			<BBB/>
			<BBB/>
			<EEE/>
			<FFF/>
		</DDD>
	</CCC>
	<CCC>
		<BBB>
			<BBB>
				<BBB/>
			</BBB>
		</BBB>
	</CCC>
</AAA>
<AAA>
	<XXX>
		<DDD>
			<BBB/>
			<BBB/>
			<EEE/>
			<FFF/>
		</DDD>
	</XXX>
	<CCC>
		<DDD>
			<BBB/>
			<BBB/>
			<EEE/>
			<FFF/>
		</DDD>
	</CCC>
	<CCC>
		<BBB>
			<BBB>
			<BBB/>
			</BBB>
		</BBB>
	</CCC>
</AAA>
<AAA>
	<XXX>
		<DDD>
			<BBB/>
			<BBB/>
			<EEE/>
			<FFF/>
		</DDD>
	</XXX>
	<CCC>
		<DDD>
			<BBB/>
			<BBB/>
			<EEE/>
			<FFF/>
		</DDD>
	</CCC>
	<CCC>
		<BBB>
			<BBB>
				<BBB/>
			</BBB>
		</BBB>
	</CCC>
</AAA>

Выражение в квадратных скобках позволяет задавать порядковые критерии для элемента. Так число в квадратных скобках обозначает позицию элемента в выбранном множестве. Функция last() позволяет указать последний элемент в выборке.

/AAA/BBB[2] /AAA/BBB[last()]
<AAA>
	<BBB/>
	<BBB/>
	<BBB/>
	<BBB/>
</AAA>
<AAA>
	<BBB/>
	<BBB/>
	<BBB/>
	<BBB/>
</AAA>

Атрибуты определяются префиксом @

//@id //BBB[@id] //BBB[@name]
<AAA>
	<BBB id="b1"/>
	<BBB id="b2"/>
	<BBB name="bbb"/>
	<BBB/>
	<CCC id="c1"/>
	<CCC/>
</AAA>
<AAA>
	<BBB id="b1"/>
	<BBB id="b2"/>
	<BBB name="bbb"/>
	<BBB/>
	<CCC id="c1"/>
	<CCC/>
</AAA>
<AAA>
	<BBB id="b1"/>
	<BBB id="b2"/>
	<BBB name="bbb"/>
	<BBB/>
	<CCC id="c1"/>
	<CCC/>
</AAA>

Значения атрибутов могут служить критерием при выборке. Функция normalize-space() удаляет пробелы в начале и конце параметра, а все последовательности пробельных символов заменяет одиночным пробелом.

//BBB[@id='b1'] //BBB[@name='bbb'] //BBB[normalize-space(@name)='bbb']
<AAA>
	<BBB id="b1"/>
	<BBB name=" bbb "/>
	<BBB name="bbb"/>
</AAA>
<AAA>
	<BBB id="b1"/>
	<BBB name=" bbb "/>
	<BBB name="bbb"/>
</AAA>
<AAA>
	<BBB id="b1"/>
	<BBB name=" bbb "/>
	<BBB name="bbb"/>
</AAA>

Функция count() возвращает число элементов в указанном множестве.

//*[count(*)=3]
выбрать все, имеющие три дочерних элемента
//*[count(BBB)=2]
выбрать все, имеющие два дочерних элемента BBB
<AAA>
	<CCC>
		<BBB/>
		<BBB/>
		<BBB/>
	</CCC>
	<DDD>
		<BBB/>
		<BBB/>
	</DDD>
	<EEE>
		<CCC/>
		<DDD/>
	</EEE>
</AAA>
<AAA>
	<CCC>
		<BBB/>
		<BBB/>
		<BBB/>
	</CCC>
	<DDD>
		<BBB/>
		<BBB/>
	</DDD>
	<EEE>
		<CCC/>
		<DDD/>
	</EEE>
</AAA>

Функция name() возвращает имя элемента. Функция starts-with() возвращает истину, если строка первого аргумента начинается строкой второго аргумента. Функция contains() возвращает истину, если строка из первого аргумента содержит строку из второго.

//*[name()='BBB']
выбрать все с именем BBB, эквивалентно пути //BBB
//*[starts-with(name(),'B')]
выбрать все, имя которых начинается с B
//*[contains(name(),'C')]
выбрать все, имя которых содержит C
<AAA>
	<BCC>
		<BBB/>
		<BBB/>
		<BBB/>
	</BCC>
	<DDB>
		<BBB/>
		<BBB/>
	</DDB>
	<BEC>
		<CCC/>
		<DBD/>
	</BEC>
</AAA>
<AAA>
	<BCC>
		<BBB/>
		<BBB/>
		<BBB/>
	</BCC>
	<DDB>
		<BBB/>
		<BBB/>
	</DDB>
	<BEC>
		<CCC/>
		<DBD/>
	</BEC>
</AAA>
<AAA>
	<BCC>
		<BBB/>
		<BBB/>
		<BBB/>
	</BCC>
	<DDB>
		<BBB/>
		<BBB/>
	</DDB>
	<BEC>
		<CCC/>
		<DBD/>
	</BEC>
</AAA>

Функция string-length() возвращает число символов в строке.

//*[string-length(name()) = 3]
выбрать все, имя которых состоит из трех символов
//*[string-length(name()) < 3]
выбрать все, с именем меньше трех символов
//*[string-length(name()) > 3]
выбрать все, с именем больше трех символов
<AAA>
	<Q/>
	<SSSS/>
	<BB/>
	<CCC/>
	<DDDDDDDD/>
	<EEEE/>
</AAA>
<AAA>
	<Q/>
	<SSSS/>
	<BB/>
	<CCC/>
	<DDDDDDDD/>
	<EEEE/>
</AAA>
<AAA>
	<Q/>
	<SSSS/>
	<BB/>
	<CCC/>
	<DDDDDDDD/>
	<EEEE/>
</AAA>

Несколько путей можно объединить с помощью разделителя |

/AAA/EEE | //BBB /AAA/EEE | //DDD/CCC | /AAA | //BBB
<AAA>
	<BBB/>
	<CCC/>
	<DDD>
		<CCC/>
	</DDD>
	<EEE/>
</AAA>
<AAA>
	<BBB/>
	<CCC/>
	<DDD>
		<CCC/>
	</DDD>
	<EEE/>
</AAA>

Ось descendant содержит всех потомков контекстного узла.

/AAA/BBB/descendant::*
выбрать всех потомков узла AAA/BBB
//CCC/descendant::*
выбрать всех потомков узла CCC
//CCC/descendant::DDD
выбрать узел DDD, если он потомок CCC
<AAA>
	<BBB>
		<DDD>
			<CCC>
				<DDD/>
				<EEE/>
			</CCC>
		</DDD>
	</BBB>
	<CCC>
		<GGG>
			<EEE>
				<DDD>
					<FFF/>
				</DDD>
			</EEE>
		</GGG>
	</CCC>
</AAA>
<AAA>
	<BBB>
		<DDD>
			<CCC>
				<DDD/>
				<EEE/>
			</CCC>
		</DDD>
	</BBB>
	<CCC>
		<GGG>
			<EEE>
				<DDD>
					<FFF/>
				</DDD>
			</EEE>
		</GGG>
	</CCC>
</AAA>
<AAA>
	<BBB>
		<DDD>
			<CCC>
				<DDD/>
				<EEE/>
			</CCC>
		</DDD>
	</BBB>
	<CCC>
		<GGG>
			<EEE>
				<DDD>
					<FFF/>
				</DDD>
			</EEE>
		</GGG>
	</CCC>
</AAA>

Ось parent содержит родителя контекстного узла, если он существует.

//DDD/parent::*
выбрать родителей DDD
//DDD/parent::BBB
выбрать BBB, если он родитель DDD
<AAA>
	<BBB>
		<DDD>
			<CCC>
				<EEE/>
				<FFF/>
			</CCC>
		</DDD>
	</BBB>
	<CCC>
		<GGG>
			<EEE>
				<DDD>
					<FFF/>
				</DDD>
			</EEE>
		</GGG>
	</CCC>
</AAA>
<AAA>
	<BBB>
		<DDD>
			<CCC>
				<EEE/>
				<FFF/>
			</CCC>
		</DDD>
	</BBB>
	<CCC>
		<GGG>
			<EEE>
				<DDD>
					<FFF/>
				</DDD>
			</EEE>
		</GGG>
	</CCC>
</AAA>

Ось ancestor содержит всех предков контекстного узла. Таким образом, эта ось всегда содержит корневой узел.

/AAA/BBB/DDD/CCC/EEE/ancestor::* //FFF/ancestor::*
<AAA>
	<BBB>
		<DDD>
			<CCC>
				<DDD/>
				<EEE/>
			</CCC>
		</DDD>
	</BBB>
	<CCC>
		<DDD>
			<EEE>
				<DDD>
					<FFF/>
				</DDD>
			</EEE>
		</DDD>
	</CCC>
</AAA>
<AAA>
	<BBB>
		<DDD>
			<CCC>
				<DDD/>
				<EEE/>
			</CCC>
		</DDD>
	</BBB>
	<CCC>
		<DDD>
			<EEE>
				<DDD>
					<FFF/>
				</DDD>
			</EEE>
		</DDD>
	</CCC>
</AAA>

Ось following-sibling содержит все последующих братьев контекстного узла. Братьями называются те узлы, что имеют одного родителя с контекстным.

/AAA/BBB/following-sibling::* //CCC/following-sibling::*
<AAA>
	<BBB>
		<CCC/>
		<DDD/>
	</BBB>
	<XXX>
		<DDD>
			<EEE/>
			<DDD/>
			<CCC/>
			<FFF/>
			<FFF>
				<GGG/>
			</FFF>
		</DDD>
	</XXX>
	<CCC>
		<DDD/>
	</CCC>
</AAA>
<AAA>
	<BBB>
		<CCC/>
		<DDD/>
	</BBB>
	<XXX>
		<DDD>
			<EEE/>
			<DDD/>
			<CCC/>
			<FFF/>
			<FFF>
				<GGG/>
			</FFF>
		</DDD>
	</XXX>
	<CCC>
		<DDD/>
	</CCC>
</AAA>

Ось preceding-sibling содержит всех предыдущих братьев контекстного узла.

/AAA/XXX/preceding-sibling::* //CCC/preceding-sibling::*
<AAA>
	<BBB>
		<CCC/>
		<DDD/>
	</BBB>
	<XXX>
		<DDD>
			<EEE/>
			<DDD/>
			<CCC/>
			<FFF/>
			<FFF>
				<GGG/>
			</FFF>
		</DDD>
	</XXX>
	<CCC>
		<DDD/>
	</CCC>
</AAA>
<AAA>
	<BBB>
		<CCC/>
		<DDD/>
	</BBB>
	<XXX>
		<DDD>
			<EEE/>
			<DDD/>
			<CCC/>
			<FFF/>
			<FFF>
				<GGG/>
			</FFF>
		</DDD>
	</XXX>
	<CCC>
		<DDD/>
	</CCC>
</AAA>

Ось following содержит все узлы, идущие после контекстного, в том же порядке, что и в самом документе, за исключением всех предков, узлов атрибутов и пространств имен.

/AAA/XXX/DDD/FFF/following::* //ZZZ/following::*
<AAA>
	<BBB>
		<CCC/>
		<ZZZ>
			<DDD/>
			<DDD>
				<EEE/>
			</DDD>
		</ZZZ>
		<FFF>
			<GGG/>
		</FFF>
	</BBB>
	<XXX>
		<DDD>
			<EEE/>
			<DDD/>
			<CCC/>
			<FFF/>
			<HHH>
				<GGG/>
			</HHH>
		</DDD>
	</XXX>
	<CCC>
		<DDD/>
	</CCC>
</AAA>
<AAA>
	<BBB>
		<CCC/>
		<ZZZ>
			<DDD/>
			<DDD>
				<EEE/>
			</DDD>
		</ZZZ>
		<FFF>
			<GGG/>
		</FFF>
	</BBB>
	<XXX>
		<DDD>
			<EEE/>
			<DDD/>
			<CCC/>
			<FFF/>
			<HHH>
				<GGG/>
			</HHH>
		</DDD>
	</XXX>
	<CCC>
		<DDD/>
	</CCC>
</AAA>

Ось preceding содержит все узлы, идущие перед контекстным узлом, в том же порядке, что и в самом документе, исключая любых предков, узлов атрибутов и пространств имен.

/AAA/XXX/preceding::* //GGG/preceding::*
<AAA>
	<BBB>
		<CCC/>
		<ZZZ>
			<DDD/>
		</ZZZ>
	</BBB>
	<XXX>
		<DDD>
			<EEE/>
			<DDD/>
			<CCC/>
			<FFF/>
			<FFF>
				<GGG/>
			</FFF>
		</DDD>
	</XXX>
	<CCC>
		<DDD/>
	</CCC>
</AAA>
<AAA>
	<BBB>
		<CCC/>
		<ZZZ>
			<DDD/>
		</ZZZ>
	</BBB>
	<XXX>
		<DDD>
			<EEE/>
			<DDD/>
			<CCC/>
			<FFF/>
			<FFF>
				<GGG/>
			</FFF>
		</DDD>
	</XXX>
	<CCC>
		<DDD/>
	</CCC>
</AAA>

Ось descendant-or-self содержит контекстный узел и всех его потомков.

/AAA/XXX/descendant-or-self::* //CCC/descendant-or-self::*
<AAA>
	<BBB>
		<CCC/>
		<ZZZ>
			<DDD/>
		</ZZZ>
	</BBB>
	<XXX>
		<DDD>
			<EEE/>
			<DDD/>
			<CCC/>
			<FFF/>
			<FFF>
				<GGG/>
			</FFF>
		</DDD>
	</XXX>
	<CCC>
		<DDD/>
	</CCC>
</AAA>
<AAA>
	<BBB>
		<CCC/>
		<ZZZ>
			<DDD/>
		</ZZZ>
	</BBB>
	<XXX>
		<DDD>
			<EEE/>
			<DDD/>
			<CCC/>
			<FFF/>
			<FFF>
				<GGG/>
			</FFF>
		</DDD>
	</XXX>
	<CCC>
		<DDD/>
	</CCC>
</AAA>

Ось ancestor-or-self содержит контекстный узел и всех его предков. Таким образом, эта ось всегда содержит корневой узел.

/AAA/XXX/DDD/EEE/ancestor-or-self::* //GGG/ancestor-or-self::*
<AAA>
	<BBB>
		<CCC/>
		<ZZZ>
			<DDD/>
		</ZZZ>
	</BBB>
	<XXX>
		<DDD>
			<EEE/>
			<DDD/>
			<CCC/>
			<FFF/>
			<FFF>
				<GGG/>
			</FFF>
		</DDD>
	</XXX>
	<CCC>
		<DDD/>
	</CCC>
</AAA>
<AAA>
	<BBB>
		<CCC/>
		<ZZZ>
			<DDD/>
		</ZZZ>
	</BBB>
	<XXX>
		<DDD>
			<EEE/>
			<DDD/>
			<CCC/>
			<FFF/>
			<FFF>
				<GGG/>
			</FFF>
		</DDD>
	</XXX>
	<CCC>
		<DDD/>
	</CCC>
</AAA>

Оператор div производит деление, оператор mod возвращает остаток от деления нацело. Функция floor - округление до ближайшего целого в сторону увеличения. Функция ceiling - округление до ближайшего целого в сторону уменьшения.

//BBB[position() mod 2 = 0]
выбрать все BBB, позиция которых кратна 2
//BBB[position() = floor(last() div 2 + 0.5) or position() = ceiling(last() div 2 + 0.5)]
выбрать BBB в середине среди всех BBB (если чётное кол-во BBB, то будут выбраны два)
//CCC[position() = floor(last() div 2 + 0.5) or position() = ceiling(last() div 2 + 0.5)]
выбрать CCC в середине среди всех CCC (если чётное кол-во CCC, то будут выбраны два)
<AAA>
	<BBB/>
	<BBB/>
	<BBB/>
	<BBB/>
	<BBB/>
	<BBB/>
	<BBB/>
	<BBB/>
	<CCC/>
	<CCC/>
	<CCC/>
</AAA>
<AAA>
	<BBB/>
	<BBB/>
	<BBB/>
	<BBB/>
	<BBB/>
	<BBB/>
	<BBB/>
	<BBB/>
	<CCC/>
	<CCC/>
	<CCC/>
</AAA>
<AAA>
	<BBB/>
	<BBB/>
	<BBB/>
	<BBB/>
	<BBB/>
	<BBB/>
	<BBB/>
	<BBB/>
	<CCC/>
	<CCC/>
	<CCC/>
</AAA>

Всем добра!

posted on november 07, 2010, updated on september 30, 2025

Комментарии

Да, всё красиво и просто. Но всё меняется, когда используются namespace-ы...

Алексей 23.01.2011 00:10 #

Спасибо за статью, очень помогла.

Zik 02.06.2011 18:47 #

Офигенно выручила статья, очень доступно, буду клацать на рекламу пока кнопка не сотрется...

Zlodey 15.07.2011 17:03 #

В оригинале этот набор примеров лучше смотрится и воспринимается.

))) 03.04.2012 20:55 #

Просто гениально.. перерыл весь инет в поиске что-то типа LIKE и чуть не свихнулся от документаций аля www.w3.org, этопросто вынос мозга.
В этом примере изучение заняли пол минуты отсилы исраз у взакладки !!! низкий поклон

Ihor 10.05.2012 01:25 #

Спасибо БРАТАН! ВЫРУЧИЛ ОТВиЧВАЮ

ЧУВАК 01.07.2013 12:54 #

Очень хорошо! Суть. Сложное состоит из простого.

Sergey 11.12.2014 20:19 #
Отправка новых комментариев временно отключена. Извините, работаем над этим.


tags cloud