Как я использую XPath с пространством имен по умолчанию без префикса?

Каков XPath (в API C# к XDocument. XPathSelectElements (xpath, nsman), если это имеет значение) запрашивать весь MyNodes из этого документа?

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <MyNode xmlns="lcmp" attr="true">
    <subnode />
  </MyNode>
</configuration>
  • Я попробовал /configuration/MyNode который является неправильным, потому что это игнорирует пространство имен.
  • Я попробовал /configuration/lcmp:MyNode который является неправильным потому что lcmp URI, не префикс.
  • Я попробовал /configuration/{lcmp}MyNode который отказавший, потому что Additional information: '/configuration/{lcmp}MyNode' has an invalid token.

Править: Я не могу использовать mgr.AddNamespace("df", "lcmp"); поскольку некоторые отвечающие стороны предложили. Это требует, что XML, парсинг программы знает все пространства имен, которые я планирую использовать заранее. Так как это предназначено, чтобы быть применимым к любому исходному файлу, я не знаю который пространства имен вручную добавить префиксы для. Это походит {my uri} синтаксис XPath, но Microsoft не потрудилась реализовывать это... верное?

34
задан Mads Hansen 27 March 2010 в 16:42
поделиться

4 ответа

Элемент конфигурации находится в безымянном пространстве имен, а MyNode привязан к пространству имен lcmp без префикс пространства имен.

Этот оператор XPATH позволит вам обращаться к элементу MyNode без объявления пространства имен lcmp или использования префикса пространства имен в вашем XPATH:

/configuration/*[namespace-uri()='lcmp' and local-name()='MyNode']

Он соответствует любому элементу, который является дочерним элементом конфигурации , а затем использует фильтр предикатов с функциями namespace-uri () и local-name () , чтобы ограничить его в элемент MyNode .

Если вы не знаете, какие uri-пространства имен будут использоваться для элементов, вы можете сделать XPATH более общим и просто сопоставить с local-name () :

/configuration/*[local-name()='MyNode']

Однако вы рискуете сопоставить разные элементы в разных словарях (привязанных к разным uri пространств имен), которые используют одно и то же имя.

37
ответ дан 27 November 2019 в 16:45
поделиться

Вам необходимо использовать XmlNamespaceManager следующим образом:

   XDocument doc = XDocument.Load(@"..\..\XMLFile1.xml");
   XmlNamespaceManager mgr = new XmlNamespaceManager(new NameTable());
   mgr.AddNamespace("df", "lcmp");
   foreach (XElement myNode in doc.XPathSelectElements("configuration/df:MyNode", mgr))
   {
       Console.WriteLine(myNode.Attribute("attr").Value);
   }
12
ответ дан 27 November 2019 в 16:45
поделиться

XPath (намеренно) не предназначен для случая, когда вы хотите использовать одно и то же выражение XPath для некоторых неизвестных пространств имен, которые существуют только в документе XML. .Предполагается, что вы заранее знаете пространство имен, объявляете пространство имен процессору XPath и используете это имя в своем выражении. Ответы Мартина и Дэна показывают, как это сделать на C #.

Причина этой трудности лучше всего выражена в спецификации XML namespaces :

Мы предполагаем приложения Extensible Markup Language (XML), в которых один XML-документ может содержать элементы и атрибуты (здесь упоминается как как «словарь разметки»), которые определены и используются несколькими программными модулями. Одним из мотивов этого является модульность: если существует такой словарь разметки, который хорошо понят и для которого доступно полезное программное обеспечение, лучше повторно использовать эту разметку, чем изобретать ее заново.

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

Эти соображения требуют, чтобы у конструкций документа были имена, построенные так, чтобы избежать конфликтов между именами из разных словарей разметки. Эта спецификация описывает механизм, пространства имен XML, который выполняет это путем присвоения расширенных имен элементам и атрибутам.

То есть пространства имен должны использоваться, чтобы убедиться, что вы знаете, о чем говорит ваш документ: это тот элемент , говорящий о преамбуле к документу XHTML или о чьей-то голове в AnatomyML документ? Вы никогда не должны "быть агностиком" в отношении пространства имен, и это в значительной степени первое, что вы должны определить в любом словаре XML.

Должно быть возможно делать то, что вы хотите, но я не думаю, что это можно сделать в одном выражении XPath. Прежде всего, вам нужно порыться в документе и извлечь все URI пространств имен, затем добавить их в диспетчер пространств имен, а затем запустить фактическое выражение XPath, которое вы хотите (и вам нужно кое-что знать о распределении пространств имен в документе на этом точка, или вам нужно выполнить много выражений). Я думаю, вам, вероятно, лучше всего использовать что-то другое, кроме XPath (например, DOM или SAX-подобный API), чтобы найти URI пространства имен, но вы также можете изучить ось пространства имен XPath (в XPath 1.0), используйте namespace-uri -from-QName (в XPath 2.0) или используйте такие выражения, как Олег "configuration / * [local-name () = 'MyNode']" . В любом случае, я думаю, что лучше всего попытаться избежать написания XPath, не зависящего от пространства имен! Почему вы не знаете свое пространство имен заранее? Как вы собираетесь избежать сопоставления вещей, которые вы не собираетесь сопоставить?

Edit - вы знаете namespaceURI?

Итак, оказывается, ваш вопрос сбил нас всех с толку. По-видимому, вы знаете URI пространства имен, но не знаете префикс пространства имен, который используется в XML-документе.Действительно, в этом случае префикс пространства имен не используется, и URI становится пространством имен по умолчанию, где он определен. Важно знать, что выбранный префикс (или отсутствие префикса) не имеет отношения к вашему выражению XPath (и синтаксическому анализу XML в целом). Атрибут prefix / xmlns - это всего лишь один из способов связать узел с URI пространства имен, когда документ выражен как текст. Возможно, вы захотите взглянуть на этот ответ , где я пытаюсь уточнить префиксы пространства имен.

Вы должны пытаться думать о XML-документе так же, как его думает синтаксический анализатор - каждый узел имеет URI пространства имен и локальное имя. Правила префикса / наследования пространства имен просто экономят много раз вводить URI. Один из способов записать это в нотации Кларка: то есть вы пишете { http://www.example.com/namespace/example } LocalNodeName, но эта нотация обычно используется только для документации - XPath ничего не знает об этих обозначениях.

Вместо этого XPath использует собственные префиксы пространства имен. Примерно так / ns1: root / ns2: node . Но они полностью отделены от префиксов, которые могут использоваться в исходном XML-документе, и не имеют ничего общего с ними. Любая реализация XPath будет иметь способ сопоставить собственные префиксы с URI пространства имен. Для реализации C # вы используете XmlNamespaceManager , в Perl вы предоставляете хэш, xmllint принимает аргументы командной строки ... Поэтому все, что вам нужно сделать, это создать произвольный префикс для URI пространства имен, который вы знаете, и использовать этот префикс в выражении XPath. Неважно, какой префикс вы используете, в XML вас просто заботит комбинация URI и localName.

Еще одна вещь, которую следует помнить (часто это удивительно), заключается в том, что XPath не выполняет наследование пространств имен. Вам нужно добавить префикс для каждого, у которого есть пространство имен, независимо от того, происходит ли пространство имен от наследования, атрибута xmlns или префикса пространства имен. Кроме того, хотя вы всегда должны думать в терминах URI и localNames, есть также способы доступа к префиксу из XML-документа. Их редко приходится использовать.

7
ответ дан 27 November 2019 в 16:45
поделиться

Вот пример того, как сделать пространство имен доступным для выражения XPath в методе расширения XPathSelectElements:

using System;
using System.Xml.Linq;
using System.Xml.XPath;
using System.Xml;
namespace XPathExpt
{
 class Program
 {
   static void Main(string[] args)
   {
     XElement cfg = XElement.Parse(
       @"<configuration>
          <MyNode xmlns=""lcmp"" attr=""true"">
            <subnode />
          </MyNode>
         </configuration>");
     XmlNameTable nameTable = new NameTable();
     var nsMgr = new XmlNamespaceManager(nameTable);
     // Tell the namespace manager about the namespace
     // of interest (lcmp), and give it a prefix (pfx) that we'll
     // use to refer to it in XPath expressions. 
     // Note that the prefix choice is pretty arbitrary at 
     // this point.
     nsMgr.AddNamespace("pfx", "lcmp");
     foreach (var el in cfg.XPathSelectElements("//pfx:MyNode", nsMgr))
     {
         Console.WriteLine("Found element named {0}", el.Name);
     }
   }
 }
}
4
ответ дан 27 November 2019 в 16:45
поделиться
Другие вопросы по тегам:

Похожие вопросы: