XPath to return default value if node not present

2020-01-30 12:05发布

问题:

Say I have a pair of XML documents

<Foo>
    <Bar/>
    <Baz>mystring</Baz>
</Foo>

and

<Foo>
    <Bar/>
</Foo>

I want an XPath (Version 1.0 only) that returns "mystring" for the first document and "not-found" for the second. I tried

(string('not-found') | //Baz)[last()]

but the left hand side of the union isn't a node-set

回答1:

In XPath 1.0, use:

concat(/Foo/Baz,
       substring('not-found', 1 div not(/Foo/Baz)))

If you want to handle the posible empty Baz element, use:

concat(/Foo/Baz,
       substring('not-found', 1 div not(/Foo/Baz[node()])))

With this input:

<Foo>
    <Baz/>
</Foo>

Result: not-found string data type.



回答2:

Special case: If you want to get 0 if numeric node is missing or empty, use "sum(/Foo/Baz)" function



回答3:

@Alejandro provided the best XPath 1.0 answer, which has been known for years, since first used by Jeni Tennison almost ten years ago.

The only problem with this expression is its shiny elegance, which makes it difficult to understand by not only novice programmers.

In a hosted XPath 1.0 (and every XPath is hosted!) one can use more understandable expressions:

string((/Foo/Baz | $vDefaults[not(/Foo/Baz/text())]/Foo/Baz)[last())

Here the variable $vDefaults is a separate document that has the same structure as the primary XML document, and whose text nodes contain default values.

Or, if XSLT is the hosting language, one can use the document() function:

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my">
 <xsl:output  method="text"/>

 <my:defaults>
    <Foo>
        <Bar/>
        <Baz>not-found</Baz>
  </Foo>
 </my:defaults>

 <xsl:template match="/">
     <xsl:value-of select=
     "concat(/Foo/Baz,
           document('')[not(current()/Foo/Baz/text())]
                                        /*/my:defaults/Foo/Baz
           )"/>
 </xsl:template>
</xsl:stylesheet>

Or, not using concat():

<xsl:stylesheet version="1.0"
 xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
 xmlns:my="my:my">
 <xsl:output  method="text"/>

 <my:defaults>
    <Foo>
        <Bar/>
        <Baz>not-found</Baz>
  </Foo>
 </my:defaults>

 <xsl:variable name="vDefaults" select="document('')/*/my:defaults"/>

 <xsl:template match="/">
     <xsl:value-of select=
     "(/Foo/Baz 
       | $vDefaults/Foo/Baz[not(current()/Foo/Baz/text())]
       )
        [last()]"/>
 </xsl:template>
</xsl:stylesheet>


回答4:

/Foo/(Baz/string(), 'not-found')[1]


回答5:

If you are okay with printing an empty string instead of 'not-found' message then use:

/Foo/concat(Baz/text(), '')

Later, you can replace the empty strings with 'not-found'.



标签: xpath