XPath to return default value if node not present

2020-01-30 11:32发布

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

标签: xpath
5条回答
我想做一个坏孩纸
2楼-- · 2020-01-30 11:49

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

查看更多
放荡不羁爱自由
3楼-- · 2020-01-30 11:54

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.

查看更多
你好瞎i
4楼-- · 2020-01-30 11:55

@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>
查看更多
混吃等死
5楼-- · 2020-01-30 12:02

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'.

查看更多
Animai°情兽
6楼-- · 2020-01-30 12:05
/Foo/(Baz/string(), 'not-found')[1]
查看更多
登录 后发表回答