【问题标题】:Unable to retrive XML elements value using XPATH when element has attributes当元素具有属性时,无法使用 XPATH 检索 XML 元素值
【发布时间】:2014-03-24 01:24:07
【问题描述】:

我必须抓取给定 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>

请注意,我必须手动删除所有属性才能使 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 

更新:

似乎只有属性为 without 前缀的属性会导致问题。例如,如果属性是xlmns="...........",那么我无法使用 XPATH 获取它的值,如果属性是 xlmns:i=".........." 那么它似乎可以工作。不知道发生了什么。

【问题讨论】:

    标签: sql sql-server sql-server-2008 tsql sqlxml


    【解决方案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)'; 
    

    【讨论】:

    • 如前所述,我无法知道公司范围内定义的自定义命名空间的所有可能组合!这不切实际。这行不通。有没有办法在@xml 中获取使用给定xml 的命名空间,然后在WITH XMLNAMESPACES ... 查询中使用它们?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-12-21
    • 1970-01-01
    • 1970-01-01
    • 2016-05-04
    • 1970-01-01
    • 2021-06-01
    • 2018-10-13
    相关资源
    最近更新 更多