Reputation:
Below is the XML file -
<Chapters>
<Chapter>
<Name>Introduction</Name>
<Heads>
<Head>
<No>1</No>
<Title>History of Internet</Title>
<Desc>
..............
</Desc>
</Head>
<Head>
<No>2</No>
<Title>History of HTML</Title>
<Desc>
..............
</Desc>
</Head>
</Heads>
</Chapter>
<Chapter>
<Name>Learn HTML</Name>
<Heads>
<Head>
<No>1</No>
<Title>Browsers</Title>
<Desc>
..............
</Desc>
</Head>
<Head>
<No>2</No>
<Title>Browser War</Title>
<Desc>
..............
</Desc>
</Head>
<Head>
<No>3</No>
<Title>HTML, DHTML</Title>
<Desc>
..............
</Desc>
</Head>
</Heads>
</Chapter>
</Chapters>
I want to list the Chapters/Chapter/Name=Introduction and Chapters/Chapter/Heads/Head/No=1 Following is the query which I am executing in baseX -
/Chapters/Chapter[contains(Name,'Introduction') and contains(Heads/Head/No,'1')]/Heads/Head/Title
And this is the error -
Query: Chapters/Chapter[contains(Name,'Introduction') and contains(Heads/Head/No,'1')]/Heads/Head/Title
Error: [XPTY0004] Single item expected, (element No { ... }, element No { ... }) found.
As per the baseX website, description of the Error XPTY0004 is -
This error is raised if an expression has the wrong type, or cannot be cast into the specified type. It may be raised both statically (during query compilation) or dynamically (at runtime).
What wrong m i doing??
Upvotes: 3
Views: 1537
Reputation: 1577
Just a follow-up for the sake of completeness and others coping with the same problem:
Your code will not work, as already pointed out contains
does not accept a sequence of strings as its first argument.
(: ssce: :)
let $doc := <doc>
<a>
<b>foo</b>
<b>bar</b>
</a>
</doc>
return $doc/a[contains(b, "bar")]
But there is an easy construct to overcome this.
(: ssce, this time working: :)
let $doc := <doc>
<a>
<b>foo</b>
<b>bar</b>
</a>
</doc>
return $doc/a[some $b in b/text() satisfies contains($b, "bar")]
These so called quantified expressions (some $var in expr
and every $var in expr
) are a concise way to express what you are actually doing.
Upvotes: 2
Reputation: 243459
/Chapters/Chapter
[contains(Name,'Introduction')
and
contains(Heads/Head/No,'1')
]
/Heads/Head/Title
The first argument of contains()
must be a single string but for the second reference to contains()
in the above expression, the argument Heads/Head/No
evaluates to two elements.
A correction of the expression would be:
/Chapters/Chapter
[Name[contains(.,'Introduction')]
and
Heads/Head/No[contains(.,'1')]
]
/Heads/Head/Title
Remark: It seems that you actually need to test for equality -- not for containment. This can be expressed in many ways, one of which is:
/Chapters/Chapter
[Name eq 'Introduction']
and
Heads/Head/No[. eq '1']
]
/Heads/Head/Title
Upvotes: 4
Reputation: 38662
fn:contains($arg1 as xs:string?, $arg2 as xs:string?) as xs:boolean
tests if character string $arg2
is contained in $arg1
. It expects single strings for each argument (or the empty value ()
).
A path step (here: Heads/Head/Title
) returns all elements which fit, so all titles of all heads. If you remove the second head of Chapter "Introduction", your query will run successfully.
contains
is the wrong way to do this. =
compares on a set semantics. Use John's solution or this one which is more close to yours (but John's will be probably faster):
/Chapters/Chapter[Name='Introduction' and Heads/Head/No='1']/Heads/Head/Title
Upvotes: 2
Reputation: 2852
This one would solve the purpose
/Chapters/Chapter[Name='Introduction']/Heads/Head[No='1']/Title
but I cant answer your question... ie I cant explain why your query resulted in error!!
Upvotes: 1