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

StackOverflow https://stackoverflow.com/questions/122463

  •  02-07-2019
  •  | 
  •  

Вопрос

У меня есть XML-файл, который начинается так:

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

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

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

-- ИЛИ --

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

Это было полезно?

Решение

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

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

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

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

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

namespace-uri(/*)

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

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

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

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

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

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

Другие советы

Это 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>

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

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top