ASP.NET Выбор узлов XML на основе значения атрибута в дочернем элементе

Я пытаюсь просмотреть приведенный ниже XML-код и найти все элементы entry, в которых базовый элемент category имеет значение collection для атрибута thr term. В приведенном ниже примере их 2. А затем получите значение атрибута href в элементе link. Однако я не могу найти подходящие селекторы:

<feed xmlns="http://www.w3.org/2005/Atom">
    <title>demo</title>
    <id>urn:uuid:071d9650-ae6c-11e7-8f1a-0800200c9a66</id>
    <link rel="self" href="https://test.com/atom/index.xml"/>
    <updated>2017-10-11T14:37:33+02:00</updated>
    <author>
        <name>Test</name>
        <uri>http://www.test.com</uri>
    </author>
    <generator version="1.8">Agent</generator>
    <entry>
        <title>YDEMO</title>
        <id>urn:uuid:15f44340-ae6c-11e7-8f1a-0800200c9a66</id>
        <category term="collection"/>
        <published>2017-10-11T13:41:53+02:00</published>
        <updated>2017-10-11T14:37:33+02:00</updated>
        <link rel="alternate" href="https://www.myurl.com" type="text/xml"/>
        <mcp:projectScenario xmlns:mcp="http://webservice.yes-co.nl/3mcp/1.5/atom-extension">NBvh</mcp:projectScenario>
    </entry>
    <entry>
        <title>DEMO 2</title>
        <id>urn:uuid:25f44340-ae6c-11e7-8f1a-0800200c9a00</id>
        <category term="collection"/>
        <published>2017-10-11T13:42:53+02:00</published>
        <updated>2017-10-11T14:38:33+02:00</updated>
        <link rel="alternate" href="https://www.myurl2.com" type="text/xml"/>
        <mcp:projectScenario xmlns:mcp="http://webservice.yes-co.nl/3mcp/1.5/atom-extension">BBvh</mcp:projectScenario>
    </entry>
    <entry>
        <title>photo</title>
        <id>12</id>
        <category term="metadata"/>
        <updated>2016-10-11T14:38:33+02:00</updated>
        <link rel="alternate" href="https://www.myurl2.com" type="text/xml"/>
    </entry>
    <entry
        xmlns:mcp="http://webservice.yes-co.nl/3mcp/1.5/atom-extension">
        <title>No title</title>
        <id>urn:uuid:6d65c57f-621f-4c15-8a1d-5dc967423d5d</id>
        <category term="media"/>
        <published>2017-10-11T13:39:43+02:00</published>
        <updated>2017-10-11T13:39:43+02:00</updated>
        <link
            xmlns:mcp="http://webservice.yes-co.nl/3mcp/1.5/atom-extension" rel="related" href="https://webservice.yes-co.com/3mcp/1.5/15f44340-ae6c-11e7-8f1a-0800200c9a66/media/6d65c57f-621f-4c15-8a1d-5dc967423d5d-large.jpg" type="image/jpg" mcp:mediaFormat="large"/>
    </entry>        
</feed>

Вот мой код, но даже несмотря на то, что переменная data содержит указанный выше XML, строка nodeList.Count возвращает 0 результатов:

    Dim WC As New WebClient
    Dim data As String = WC.DownloadString("http://localhost/index.xml")

    Dim indexXML As New XmlDocument
    indexXML.LoadXml(data)

    Dim mgr As XmlNamespaceManager = New XmlNamespaceManager(indexXML.NameTable)
    mgr.AddNamespace("http://www.w3.org/2005/Atom", indexXML.DocumentElement.NamespaceURI)

    Dim node As XmlNode

    Dim root As XmlNode = indexXML.DocumentElement
    Dim nodeList As XmlNodeList = root.SelectNodes("/feed/entry")

    'now loop through all elements  with "category term=collection" in index.xml
    For i As Integer = 0 To nodeList.Count - 1
        If nodeList(i).SelectSingleNode("/category/@term=collection") IsNot Nothing Then
            LogMessage(nodeList(i).SelectSingleNode("/category/link/@href").Value)
        End If
    Next i  

ОБНОВЛЕНИЕ 1
Я хочу выбрать все элементы 'entry', в которых есть узел категории с term=collection. Эта часть работает через этот оператор: indexXML.SelectNodes("/atom:feed/atom:entry[atom:category/@term=""collection""]", mgr)

Я хочу начать с входного узла, а затем я хочу выбрать атрибут href ссылки подэлемента входа (и в будущем другие дочерние элементы entry. Однако ни один из примеров, которые я пробовал ниже, не возвращает значение атрибута href . Как я могу это исправить?

Теперь у меня есть это:

Dim mgr As XmlNamespaceManager = New XmlNamespaceManager(indexXML.NameTable)
mgr.AddNamespace("atom", "http://www.w3.org/2005/Atom")

Dim root As XmlNode = indexXML.DocumentElement
Dim nodeList As XmlNodeList = indexXML.SelectNodes("/atom:feed/atom:entry[atom:category/@term=""collection""]", mgr)

'now loop through all collections in index.xml

For i As Integer = 0 To nodeList.Count - 1 '1 result found

'NONE OF CALLS BELOW RETURN THE VALUE OF HREF ATTRIBUTE
    If nodeList(i).SelectSingleNode("atom:/link/@href", mgr) IsNot Nothing Then
        LogMessage(nodeList(i).SelectSingleNode("atom:/link/@href", mgr).Value)
'error: 'atom:/link/@href' has an invalid qualified name.
    End If
Next i

ОБНОВЛЕНИЕ 2 Благодаря @Pawel я смог выбрать все entry узлы, которые имеют project в качестве значения для атрибута term на узле category следующим образом:

objectsXML.SelectNodes("/atom:feed/atom:entry[atom:category/@term=""project""]", mgr)

Однако как я могу добавить к этому селектору дополнительный критерий для фильтрации entry узлов, имеющих значение NBvh ИЛИ BBvh для узла mcp:projectScenario?

ОБНОВЛЕНИЕ 3. Я добавил к менеджеру дополнительное пространство имен:

mgr.AddNamespace("atom", "http://www.w3.org/2005/Atom")
mgr.AddNamespace("mcp", "http://webservice.yes-co.nl/3mcp/1.5/atom-extension")

Но когда я пытаюсь выбрать атрибут href медиа-элемента с помощью uuid, я получаю сообщение об ошибке: Object reference not set to an instance of an object.

Мой код:

objectsXML.SelectSingleNode("/atom:feed/atom:entry[atom:id=""urn:uuid:" + "6d65c57f-621f-4c15-8a1d-5dc967423d5d" + """]/mcp:link/@href", mgr).InnerText

person Flo    schedule 03.11.2017    source источник
comment
Понятия не имею, как должен выглядеть код в asp, но /feed/entry[category/@term="collection"]/link/@href должен вернуть вам список необходимых узлов   -  person Andersson    schedule 03.11.2017
comment
Привет, к сожалению, с этим селектором все еще 0 результатов. Кстати: причина, по которой я хотел выбрать узел entry, заключается в том, что я могу легко выбрать другие дочерние узлы (например, link, но их больше)   -  person Flo    schedule 04.11.2017


Ответы (1)


В документе используется пространство имен http://www.w3.org/2005/Atom. Вам необходимо связать это пространство имен с префиксом uri и использовать этот префикс в своем XPath. Если вы привяжете пространство имен к префиксу atom следующим образом:

var nsmanager = new XmlNamespaceManager(indexXML.NameTable);
nsmanager.AddNamespace("atom", "http://www.w3.org/2005/Atom");

Вы сможете использовать этот префикс в своих выражениях XPath, если передадите диспетчер пространства имен, например:

indexXML.SelectNodes("/atom:feed/atom:entry[atom:category/@term="collection"]/atom:link/@href", nsmanager)
person Pawel    schedule 04.11.2017
comment
Спасибо. Есть ли способ, при котором мне не нужно добавлять этот префикс atom ко всем моим XSL-выражениям, а также передавать диспетчер пространств имен? Честно говоря, я думаю, что это слишком усложняет ситуацию. Я могу представить себе настройку этого вначале, но после этого я хотел бы использовать обычные XSL-выражения без префикса ... возможно ли это? Я пробовал mgr.AddNamespace("", "http://www.w3.org/2005/Atom"), а затем Dim nodeList As XmlNodeList = root.SelectNodes("/feed/entry"), но результат все равно 0. - person Flo; 04.11.2017
comment
Есть несколько вариантов, но я не уверен, что они лучше. Вы можете использовать местные имена, например /*[local-name() = 'feed']. Вы можете перемещаться по дереву вручную, проверяя свойство LocalNode. Вы можете использовать Linq to Xml, где вы можете напрямую использовать пространство имен Uri. Вы не можете использовать пустую строку для префикса пространства имен. Кстати. в тех местах, где вы ищете category в своем коде, вы используете абсолютный путь к узлу (т.е. начиная с /, но он должен быть относительно текущего узла) - person Pawel; 04.11.2017
comment
Спасибо. Я добавил обновление 1, и пока все в порядке с префиксом. Прямо сейчас я могу выбрать элемент entry, но я хочу выбрать элемент entry, а ЗАТЕМ выбрать его дочерние элементы (в данном случае элемент link с) на основе другого селектора. Вы можете помочь? - person Flo; 04.11.2017
comment
Как я уже сказал, вы не можете помещать / перед каждым выражением XPath - потому что вы начинаете с элемента документа. Например, у вас есть /atom:link/@href, но элемент atom:link не является корневым узлом, поэтому его нельзя найти. Если вы хотите создать выражение относительно узла контекста, вам нужно иметь `atom: link / @ href. Другое выражение не работает, потому что вам не хватает префиксов пространства имен. - person Pawel; 05.11.2017
comment
Ах, пропустил. Я изменил селектор, и теперь он действительно найден. Однако для получения значения оба свойства .Value и .InnerText выдают ошибку _3 _... почему? - person Flo; 06.11.2017
comment
потому что это не atom:/link, а atom:link - person Pawel; 06.11.2017
comment
Благодарю. Теперь у меня возникла дополнительная проблема с существующим селектором. См. Обновление 2 в моем посте. Как я мог добавить этот критерий? Нужно ли мне делать это в двух отдельных выборках, или это можно сделать в одной выборке (даже если у нее другое пространство имен)? Еще раз спасибо! - person Flo; 09.11.2017
comment
@Flo Ответ на ваше продолжение: objectsXML.SelectNodes("/atom:feed/atom:entry[atom:category/@term='project' and not (mcp:projectScenario[. = 'NVBh' or . = 'BBVH'])]", mgr) - person JLRishe; 09.11.2017
comment
Спасибо @JLRishe. Этот код вызывает ошибку: '/atom:feed/atom:entry[atom:category/‌​@term='project' and not (mcp:projectScenario[. = 'NVBh' or . = 'BBVH'])]' has an invalid token. Также я НЕ хочу включать ТОЛЬКО значения «NBvh» и «BBvh», поэтому я исправил проверяемые значения и удалил оператор not из вашего примера. Результат - objectsXML.SelectNodes("/atom:feed/atom:entry[atom:category/‌​@term=""project"" and (mcp:projectScenario[. = ""NBvh"" or . = ""BBvh""])]", mgr), но все равно получаю ту же ошибку. Как я могу это исправить? - person Flo; 10.11.2017
comment
@Flo Не знаю, как, но каким-то образом между category/ и @term проскользнуло пространство нулевой ширины в Юникоде и не соединяющийся с нулевой шириной, возможно, когда вы копируете / вставляете XPath. Это действительно и должно работать (я удалил недопустимые невидимые символы): /atom:feed/atom:entry[atom:category/@term='project' and mcp:projectScenario[. = 'NBvh' or . = 'BBvh']] - person JLRishe; 10.11.2017
comment
@JLRishe странно, никогда раньше такого не видел. Но теперь я получаю еще одну ошибку: Namespace prefix 'mcp' is not defined. - person Flo; 10.11.2017
comment
@Flo Вам нужно добавить префикс mcp в диспетчер пространства имен, так же, как вы делаете с префиксом atom. - person JLRishe; 10.11.2017
comment
@JLRishe Спасибо! Я не знал, что могу добавить несколько пространств имен к одному диспетчеру пространств имен! Я сделал это сейчас, но все равно получаю ошибку, см. Обновление 3. - person Flo; 10.11.2017
comment
@Flo Путь в обновлении 3 должен содержать atom: link, а не mcp: link. Вы получаете сообщение об ошибке, потому что путь не может выбрать что-либо, а SelectSingleNode возвращает Nothing. - person JLRishe; 10.11.2017