Подтвердить что ты не робот

Как получить пространства имен в файлах XML с помощью Xpath

У меня есть XML файл, который начинается следующим образом:

<Elements name="Entities" xmlns="XS-GenerationToolElements">

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

Используя XPath, я хотел бы иметь автоматический способ добавления данного пространства имен в диспетчер пространства имен. До сих пор я мог получить пространство имен, анализируя xml файл, но у меня есть экземпляр XPathNavigator, и у него должен быть хороший и чистый способ получить пространства имен, правильно?

- ИЛИ -

Учитывая, что у меня есть только одно пространство имен, каким-то образом XPath использует только тот, который присутствует в xml, тем самым избегая загромождения кода, всегда добавляя пространство имен.

4b9b3361

Ответ 1

Есть несколько методов, которые вы можете попробовать; который вы используете, будет зависеть именно от того, какую информацию вам нужно получить из документа, насколько строго вы хотите быть и насколько совместима реализация XPath, которую вы используете.

Один из способов получить URI пространства имен, связанный с конкретным префиксом, - это использовать ось namespace::. Это даст вам пространство имен node, имя которого является префиксом и значением которого является URI пространства имен. Например, вы можете получить URI пространства имен по умолчанию в элементе документа с помощью пути:

/*/namespace::*[name()='']

Возможно, вы сможете использовать это для создания ассоциаций пространств имен для своего XPathNavigator. Однако следует предупредить, что ось namespace:: является одним из тех углов XPath 1.0, которые не всегда реализованы.

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

namespace-uri(/*)

предоставит вам это пространство имен.

Альтернативой было бы забыть об ассоциировании префикса с этим пространством имен и просто сделать свой путь без пространства имен. Вы можете сделать это, используя функцию local-name(), когда вам нужно обратиться к элементу, пространство имен которого вы не знаете. Например:

//*[local-name() = 'Element']

Вы можете пойти еще на один шаг и проверить URI пространства имен элемента на один из элементов документа, если вы действительно захотите:

//*[local-name() = 'Element' and namespace-uri() = namespace-uri(/*)]

Последний параметр, учитывая, что пространство имен, кажется, ничего не значит для вас, было бы запустить ваш XML через фильтр, который удаляет пространства имен. Тогда вам не придется беспокоиться о них в своем XPath. Самый простой способ сделать это - просто удалить атрибут xmlns с регулярным выражением, но вы могли бы сделать что-то более сложное, если бы вам нужно было делать другие приемы в то же время.

Ответ 2

Это 40-строчное преобразование xslt предоставляет всю полезную информацию об пространствах имен в данном XML-документе:

<xsl:stylesheet version="1.0"
   xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
   xmlns:ext="http://exslt.org/common"
   exclude-result-prefixes="ext"
>

<xsl:output omit-xml-declaration="yes" indent="yes"/>

<xsl:strip-space elements="*"/>

<xsl:key name="kNsByNsUri" match="ns" use="@uri"/>

<xsl:variable name="vXmlNS" 
    select="'http://www.w3.org/XML/1998/namespace'"/>

<xsl:template match="/">
  <xsl:variable name="vrtfNamespaces">
    <xsl:for-each select=
      "//namespace::*
             [not(. = $vXmlNS)
             and
              . = namespace-uri(..)
           ]">
      <ns element="{name(..)}"
          prefix="{name()}" uri="{.}"/>
    </xsl:for-each>
  </xsl:variable>

  <xsl:variable name="vNamespaces"
    select="ext:node-set($vrtfNamespaces)/*"/>

  <namespaces>
          <xsl:for-each select=
           "$vNamespaces[generate-id()
                        =
                         generate-id(key('kNsByNsUri',@uri)[1])
                        ]">
            <namespace uri="{@uri}">
              <xsl:for-each select="key('kNsByNsUri',@uri)/@element">
                <element name="{.}" prefix="{../@prefix}"/>
              </xsl:for-each>
            </namespace>
          </xsl:for-each>
  </namespaces>
</xsl:template>

При применении к следующему XML-документу:

<a xmlns="my:def1" xmlns:n1="my:n1"
   xmlns:n2="my:n2" xmlns:n3="my:n3">
  <b>
    <n1:d/>
  </b>
  <n1:c>
    <n2:e>
      <f/>
    </n2:e>
  </n1:c>
  <n2:g/>
</a>

получается желаемый результат:

<namespaces>
   <namespace uri="my:def1">
      <element name="a" prefix=""/>
      <element name="b" prefix=""/>
      <element name="f" prefix=""/>
   </namespace>
   <namespace uri="my:n1">
      <element name="n1:d" prefix="n1"/>
      <element name="n1:c" prefix="n1"/>
   </namespace>
   <namespace uri="my:n2">
      <element name="n2:e" prefix="n2"/>
      <element name="n2:g" prefix="n2"/>
   </namespace>
</namespaces>

Ответ 3

К сожалению, у XPath нет понятия "пространство имен по умолчанию". Вам необходимо зарегистрировать пространства имен с префиксами с контекстом XPath, а затем использовать эти префиксы в выражениях XPath. Это означает, что для очень многословного xpath, но это основной недостаток XPath 1. Очевидно, XPath 2 рассмотрит это, но это не поможет вам прямо сейчас.

Я предлагаю вам программно изучить ваш XML-документ для пространства имен, связать это пространство имен с префиксом в контексте XPath, а затем использовать префикс в выражениях xpath.