Пространство имен Xml ломает мой xpath! [дубликат]


этот вопрос уже есть ответ здесь:

у меня есть следующий XML-код:

<List xmlns="http://schemas.microsoft.com/sharepoint/soap/">
 <Fields>
   <Field>
   </Field>
 </Fields>
</List>

это уменьшенная версия XML, возвращаемая из веб-службы SharePoint. У меня также есть следующий xPath:

/List/Fields/Field

когда я удалить элемент xmlns из моего XML xPath работает нормально. Когда он там, мой xPath ничего не находит. Есть ли что-то, что я должен делать по-другому с моим xPath? Изменение XML не является опцией.

5 70

5 ответов:

у меня также есть следующий xPath:

/List/Fields/Field 

когда я удаляю xmlns из моего XML xPath работает нормально. Когда это происходит там мой xPath ничего не находит

если вы не можете зарегистрировать привязку пространства имен и не можете использовать (предполагая, что зарегистрированный префикс "x"):

/x:List/x:Fields/x:Field

тогда есть другой способ:

/*[name()='List']/*[name()='Fields']/*[name()='Field']

элемент списка был определен с пространством имен по умолчанию, и это принимается всеми элементами внутри.

поэтому вам нужно игнорировать пространство имен элементов следующим образом:

/*[local-name()='List']/*[local-name()='Fields]/*[local-name()='Field]

но это означает, что xpath заберет любой другой элемент с List-Fields-Field

вы можете сделать проверку пространства имен, а также проверку локального имени следующим образом:

/*[local-name()='List' and namespace-uri()='http://schemas.microsoft.com/sharepoint/soap/']/*[local-name()='Fields' and namespace-uri()='http://schemas.microsoft.com/sharepoint/soap/']/*[local-name()='Field' and namespace-uri()='http://schemas.microsoft.com/sharepoint/soap/']

или вы можете зарегистрировать пространство имен в библиотеке, а затем явно указать префикс для этого пространства имен и добавьте его в выражение xpath, метод которого зависит от используемой библиотеки.

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

например, в php (поскольку вы не указали язык) с помощью DOMXPath вы можете сделать что-то вроде этого:

$xpath = new DOMXPath($document);
$xpath->registerNamespace('x', 'http://schemas.microsoft.com/sharepoint/soap/');
$xpath->query('/x:List/x:Fields/x:Field');

у меня только что была эта проблема при использовании Xalan-c

бит, который я не совсем получил изначально, заключается в том, что псевдонимы/префиксы пространства имен XPath или XSLT могут отличаться от документа - в зависимости от вашего преобразователя пространства имен.

похоже, что если в документе есть пространство имен, то он не соответствует элементу пути, если не используется пространство имен. (стандарт, но не всегда соблюдается?)

XalanDocumentPrefixResolver будет отображать XPath или XSLT пространства имен для URI и попытаться дать им идентификатор, получив префикс-где нет префикса он использовал имя, которое превратилось в xmlns

/xmlns:List/xmlns:Fields/xmlns:Field

в качестве альтернативы вы можете создать свой собственный решатель, но он по-прежнему требует минимального пространства имен, используемого в xpath :(

вот один я взломал вместе во время тестирования, нет гарантии памяти

// don't care what prefix given, there can only be the one
struct NoPrefixResolver : public xalanc::PrefixResolver {

    NoPrefixResolver(const xalanc::XalanDOMString&   theURI) : m_uri(theURI){}

    virtual const xalanc::XalanDOMString*
        getNamespaceForPrefix(const xalanc::XalanDOMString&     prefix) const {
        return &m_uri;
    }

    virtual const xalanc::XalanDOMString&   getURI() const {
        return m_uri;
    }

    const xalanc::XalanDOMString    m_uri;
};

/x:List/x:Fields/x:Field 
/a:List/b:Fields/c:Field 

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

//Fields/Field

это работает до тех пор, пока у вас нет "полей" под любым другим узлом и пока подузлы не имеют пространства имен.