XPath 1.0 to find if an element's value is in a list of values

后端 未结 2 1159
广开言路
广开言路 2020-12-03 02:29

Is there a way to construct an XPath that evaluates whether an element\'s value is in a predefined list of values? Something akin to this:

/Location/Addr[Sta         


        
相关标签:
2条回答
  • 2020-12-03 03:03

    You can check multiple conditions inside the same square brackets:

    /Location/Addr[State='TX' or State='AL' or State='MA']
    

    Or if you have a really long list, you can create a list of states and use the contains() function.

    /Location/Addr[contains('TX AL MA', State)]
    

    This will work fine for two-letter state abbreviations. If you want to make it more robust for longer strings you could add some spaces on the ends and check for _TX_, _AL_, etc. (where the underscores are spaces).

    /Location/Addr[contains(' TX AL MA ', concat(' ', State, ' '))]
    
    0 讨论(0)
  • 2020-12-03 03:13

    Just necromancing, since XPath 2.0 has come along.

    With XPath 2.0, you can do:

    /Location/Addr[State=('TX', 'AL', 'MA')]
    

    Alternatively, with XPath 1.0, you can use contains in combination with string-length:

    DECLARE @tXML xml = '<svg>
    <g>
    <path></path>
    <path data-objid="0000X1">tt</path>
    <path data-objid="0000X2"></path>
    <path data-objid="0000X3"></path>
    </g>
    </svg>';
    
    -- SELECT @tXML;
    
    SELECT 
        c.p.value('(@data-objid)[1]', 'varchar(50)') AS objID 
    FROM @tXML.nodes('//node()[contains("0000X1,0000X2", @data-objid) and string-length(@data-objid) != 0]') AS c(p)
    
    
    SET @tXML.modify('delete //node()[contains("0000X1,0000X2", @data-objid) and string-length(@data-objid) != 0]'); 
    
    SELECT @tXML AS del;
    
    DECLARE @newRecord xml = '<path data-objid="0000X4"></path>'; 
    
    
    -- SET @tXML.modify('insert sql:variable("@newRecord") as first into (/svg/g)[1]')
    SET @tXML.modify('insert sql:variable("@newRecord") as last into (/svg/g)[1]')
    
    SELECT @tXML AS ins;
    

    See also: add block into specific position

    And to update (with xml, you can only update one value at a time, and the text()selector finds nothing for an empty element, so you first need to empty the element, then insert the value, and that for each match):

    SET @tXML.modify('replace value of (//path[contains("0000X1,0000X2", @data-objid) and string-length(@data-objid) != 0]/text())[1] with "40"')
    SET @tXML.modify('replace value of (//path[contains("0000X1,0000X2", @data-objid) and string-length(@data-objid) != 0]/text())[2] with "40"')
    
    SET @tXML.modify('replace value of (//path[contains("0000X1,0000X2", @data-objid) and string-length(@data-objid) != 0]/text())[1] with ""')
    SET @tXML.modify('insert text{"This Works"} into (//path[contains("0000X1,0000X2", @data-objid) and string-length(@data-objid) != 0])[1]')
    
    SET @tXML.modify('replace value of (//path[contains("0000X1,0000X2", @data-objid) and string-length(@data-objid) != 0]/text())[2] with ""')
    SET @tXML.modify('insert text{"This Works"} into (//path[contains("0000X1,0000X2", @data-objid) and string-length(@data-objid) != 0])[2]')
    

    And to insert and delete an attribute

    -- insert attribute
    -- SET @tXML.modify('insert attribute data-objid1 {"1"} into (//path[contains("0000X1,0000X2", @data-objid) and string-length(@data-objid) != 0])[1]')
    -- delete attribute
    -- and then, delete suddenly can remove several nodes - unlike insert or modify ... 
    -- SET @tXML.modify('delete (//path[contains("0000X1,0000X2", @data-objid) and string-length(@data-objid) != 0])/@data-objid[1]')
    
    -- only insert if there is no attribute "data-objid", therefore check with [not(@data-objid)]
    -- (on replace, it doesn't create an attribute if it doesn't exist)
    SET @tXML.modify('insert attribute data-objid {"1"} into (//path[not(@data-objid) and contains("0000X1,0000X2", @data-objid) and string-length(@data-objid) != 0])[1]')
    SET @tXML.modify('replace value of (//path[contains("0000X1,0000X2", @data-objid) and string-length(@data-objid) != 0]/@data-objid)[1] with "Test"')
    

    And with variables:

    DECLARE @testvar varchar(30); 
    SET @testvar = 'abc'; 
    SET @tXML.modify('insert attribute data-objid {sql:variable("@testvar")} into (//path[not(@data-objid)] and contains("0000X1,0000X2", @data-objid) and string-length(@data-objid) != 0)[1]')
    SET @tXML.modify('replace value of (//path[contains("0000X1,0000X2", @data-objid) and string-length(@data-objid) != 0]/@data-objid)[1] with sql:variable("@testvar")')
    SET @testvar = '0000X1,0000X2'; 
    SET @tXML.modify('delete (//path[contains(sql:variable("@testvar"), @data-objid) and string-length(@data-objid) != 0])/@data-objid[1]')
    

    Note that you should concat the attribute data-objid to ("," + objid + ","), so that modify doesn't accidentally find it if the testvar is ',0000X1a,0000X2b,' instead of ',0000X1,0000X2,' (for example)

    DECLARE @testvar varchar(30); 
    SET @testvar = ',0000X1a,0000X2b,'; 
    SET @tXML.modify('delete (//path[contains(sql:variable("@testvar"), concat(",", @data-objid, ",")) and string-length(@data-objid) != 0])/@data-objid[1]')
    
    SELECT @tXML 
    
    0 讨论(0)
提交回复
热议问题