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

Задача, которую я должен захватить элементы данного XML, сгенерировать xpath для каждого элемента и затем извлечь значения каждого элемента:

Я могу создать первый и второй шаг, но когда элемент имеет атрибуты, XPATH не работает:

так что если у меня был следующий XPATH:

/Envelope[1]/Body[1]/sVerify[1]/verifyPost[1]/Message[1]/Error[1]
/Envelope[1]/Body[1]/sVerify[1]/verifyPost[1]/scope[1]/machine[1]/space[1]
/Envelope[1]/Body[1]/sVerify[1]/verifyPost[1]/scope[1]/Date[1]

Он работает для Below XML и может правильно получить значение элементов:

-- Works: I can retrieve the Elements values using XPATH
<Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <Body>
        <sVerify>
            <verifyPost>
                <scope>
                    <machine>
                        <name>test</name>
                        <space>test2</space>
                    </machine>
                    <Sys>internal</Sys>
                    <Date>2013-02-28</Date>
                </scope>
                <Message>
                    <Error>11111111111</Error>
                    <Descrip>222222222</Descrip>
                </Message>
                <Final>true</Final> 
                <Receipt>33333</Receipt>
            </verifyPost>
        </sVerify>
    </Body>
</Envelope>

Обратите внимание, что мне пришлось вручную удалить все attirbutes, чтобы XPATH работал. Это не сработает, если XML был таким, как показано ниже:

-- Doesn't work: can't get the elements value
<Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <Body>
        <sVerify xmlns="http://www.myCompany.com/Location/2014">
            <verifyPost>
                <scope>
                    <machine xmlns:i="http://www.myCompany.com/Location/2014">
                        <name>test</name>
                        <space>test2</space>
                    </machine>
                    <Sys>internal</Sys>
                    <Date>2013-02-28</Date>
                </scope>
                <Message xmlns="http://www.myCompany.com/Location/2014">
                    <Error>11111111111</Error>
                    <Descrip>222222222</Descrip>
                </Message>
                <Final xmlns="http://www.myCompany.com/Location/2014">true</Final>  
                <Receipt>33333</Receipt>
            </verifyPost>
        </sVerify>
    </Body>
</Envelope>

Может быть любое количество атрибутов, поэтому я никогда не знаю заранее, какими будут эти атрибуты. Как правильно обеспечить, чтобы XPATH всегда находил значение данного элемента независимо от того, имеет ли он атрибуты или нет.

Ниже показано, как я выполняю его в TSQL:

DECLARE @generatedXPATH nvarchar(500),
    @elementVal nvarchar(50),
    @xml xml,
    @query nvarchar(max)

-- it works with this payload
-- because attributes aren't there replacing it with xml
-- where element attributes are present fails the element value extraction using xpath 
SET @xml = '<Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"> <Body> <sVerify> <verifyPost> <scope> <machine> <name>test</name> <space>test2</space> </machine> <Sys>internal</Sys> <Date>2013-02-28</Date> </scope> <Message> <Error>11111111111</Error> <Descrip>222222222</Descrip> </Message> <Final>true</Final> <Receipt>33333</Receipt> </verifyPost> </sVerify> </Body> </Envelope>'

SET @generatedXPATH = '/Envelope[1]/Body[1]/sVerify[1]/verifyPost[1]/scope[1]/machine[1]/space[1]'
SET @elementVal = ''
SET @query = N'SELECT @elementVal=    Nodes.node.value(''(' + @generatedXPATH
                           + ')[1]'', ''varchar(50)'')  FROM   @xml.nodes(''.'') AS Nodes(node)'; 

exec sp_ExecuteSql 
@query, 
N' @xml xml,@elementVal nvarchar(max) output', 
@xml = @xml, 
@elementVal = @elementVal output

select @elementVal 

Обновить:

Похоже, только атрибуты, где атрибут без префикса вызывает проблему. например, если атрибут xlmns="..........." тогда я не могу получить его значение с помощью XPATH, если атрибут был xlmns:i="..........", то, кажется, работает. Не уверен, что происходит.

1 ответ

Решение

Если у вас есть XML с настраиваемым пространством имен, вам нужно определить его и использовать в качестве аффикса для каждого элемента, определенного в этом пространстве имен, с помощью WITH XMLNAMESPACES ( http://technet.microsoft.com/en-us/library/ms177607.aspx) В вашем случае попробуйте это:

SET @generatedXPATH = '/Envelope[1]/Body[1]/ns:sVerify[1]/ns:verifyPost[1]/ns:scope[1]/ns:machine[1]/ns:space[1]'
SET @elementVal = ''
SET @query = N'WITH XMLNAMESPACES (''http://www.myCompany.com/Location/2014'' AS ns) 
    SELECT @elementVal=    Nodes.node.value(''(' + @generatedXPATH
                           + ')[1]'', ''varchar(50)'')  FROM   @xml.nodes(''.'') AS Nodes(node)'; 

Редактировать: Если вы не знаете пространство имен, использование * вместо ns должно работать для получения элемента независимо от пространства имен:

SET @generatedXPATH = '/Envelope[1]/Body[1]/*:sVerify[1]/*:verifyPost[1]/*:scope[1]/*:machine[1]/*:space[1]'
SET @elementVal = ''
SET @query = N'SELECT @elementVal=    Nodes.node.value(''(' + @generatedXPATH
                           + ')[1]'', ''varchar(50)'')  FROM   @xml.nodes(''.'') AS Nodes(node)'; 
Другие вопросы по тегам