Как запрашивать XML со сложными типами

Я создаю программу (Visual Studio 2010, .NET 4, консольное приложение на основе С#) для сбора конкретной информации из общедоступного правительственного отчета, который доступен только для загрузки в формате xml. Его структура аналогична следующей:

<Collections>
<Collection>
<Info id="123456" address="Some Place" name="Some Name"/>
<Items>
<Item1/>
<Item2/>
<Item3 I3="Y"/>
<Item3A I3A1="N" I3A2="N" I3A3 = "Y"/>
<Item3B I3B1="N" I3B2="N"/>
<Item4/>
</Items>
</Collection>
<Collection>...</Collection>
<Collection>...</Collection>
</Collections>

Полный файл состоит из сотен блоков и колеблется от 50 до 100 МБ. Я никогда не работал с форматом XML, хотя бы отдаленно похожим на этот (выглядит ужасно, правда?), и у меня было много проблем, пытаясь найти какие-либо примеры полезных запросов.

Мне нужно вернуть идентификатор из элемента для всех узлов, у которых есть «Y» в элементах от Item3 до Item3B. Это сводит меня с ума, потому что было бы проще, если бы у них были совпадающие имена элементов и совпадающие атрибуты, но все они уникальны. Вы не можете включать подстановочный знак в запрос XPath, например /Item3*[Q3*="Y"].

У кого-нибудь есть идеи, как с этим справиться? Спасибо!


person Sean Võ Kirkpatrick    schedule 21.11.2014    source источник


Ответы (1)


Мне нужно вернуть идентификатор из элемента для всех узлов, у которых есть «Y» в элементах от Item3 до Item3B.

Правильный ответ зависит от точных «правил» выбора узлов. Неясно, всегда ли вы ищете от Item3 до Item3B или это просто примеры правила. Я также предполагаю, что под «узлами есть« Y »в элементах» вы подразумеваете, что они имеют значение атрибута, равное «Y».

Если вас интересуют ровно три узла элементов с именами «Item3», «Item3A» и «Item3B», и если значение «Y» может быть для любого атрибута, используйте

//*[self::Item3 or self::Item3A or self::Item3B][@* = 'Y']

В противном случае, если правило говорит только о том, что имена элементов должны начинаться с «Item3», используйте

//*[starts-with(name(),'Item3')][@* = 'Y']

Если во входном XML-документе есть пространства имен, было бы безопаснее использовать функцию local-name() вместо name().

Кажется, вы также пытаетесь сопоставить атрибуты, начинающиеся с определенной строки:

//*[starts-with(name(),'Item3')][@*[starts-with(name(),'Q3')] = 'Y']

Как вы видете,

Вы не можете включать подстановочный знак в запрос XPath, например /Item3*[Q3*="Y"].

на самом деле это не так - есть "подстановочные знаки" (вы обычно не называете их подстановочными знаками), но вам нужен правильный синтаксис.

person Mathias Müller    schedule 21.11.2014
comment
Это было чрезвычайно полезно! В итоге я использовал это: //*[starts-with(name(),'Item3')][@*[starts-with(name(),'I3')] = 'Y']/ancestor::Collection /Информация/@id Спасибо! :) - person Sean Võ Kirkpatrick; 22.11.2014