[xquery-talk] Selecting everything before and after path,
but without duplicated leafs
J. Zhang
zhangjunte at gmail.com
Mon Feb 11 14:52:11 PST 2008
On Feb 11, 2008 12:08 PM, Michael Kay <mike at saxonica.com> wrote:
> Sorry, I started writing the function to return elements only and then
> changed my mind. The type signature of the function should be "as node()*"
> rather than "as element()".
Michael, I got it working in XSLT with you tip.
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">
<xsl:strip-space elements="*"/>
<xsl:output method="xml" encoding="ISO-8859-1"
doctype-public="-//W3C//DTD HTML 4.0 Transitional//EN"/>
<xsl:template match="*">
<xsl:copy>
<xsl:copy-of select="@*" />
<xsl:apply-templates/>
</xsl:copy>
</xsl:template>
<xsl:template match="/ead[1]/archdesc[1]/dsc[1]/c01[1]">
<SELECT />
<xsl:next-match/>
</xsl:template>
</xsl:stylesheet>
However, I like XQuery more. :) Simply because I want to learn XQuery,
I will ask one more question about this problem:
The function looks like this:
declare function local:processNode($e as node()) as node()*
{
typeswitch ($e)
case element(c01)
return <SELECT>{$e}</SELECT>
case element()
return
element {name($e)} {
for $c in $e/child::node() return
local:processNode($c)
}
default return $e
};
local:processNode(doc('file.xml')/*)
Instead of matching the element name, I would like to match the entire
path (node). In my case, I got the path
"/ead[1]/archdesc[1]/dsc[1]/c01[1]".
So the condition in the first case in the switch should be something else.
I assume another extra function is needed.
I already got a function that returns the path of the current node as a string:
declare function local:getEADPath ( $node as node() ) as xs:string
{
let $pathelements := for $a in $node/ancestor-or-self::*
return
if ( name($a) = "ead" ) then "/ead[1]"
else concat(name($a), "[" ,
count($a/preceding-sibling::*[name()=name($a)]) + 1, "]")
return string-join( $pathelements, "/" )
};
Thanks,
jz
> Michael Kay
> http://www.saxonica.com/
>
> > -----Original Message-----
> > From: J. Zhang [mailto:zhangjunte at gmail.com]
> > Sent: 11 February 2008 10:53
> > To: Michael Kay
> > Cc: talk at x-query.com
> > Subject: Re: [xquery-talk] Selecting everything before and
> > after path,but without duplicated leafs
> >
>
> > On Feb 11, 2008 10:07 AM, Michael Kay <mike at saxonica.com> wrote:
> > > > > what output would you like to see?
> > > >
> > > > I would like to preserve the original XML file, but only add an
> > > > extra tag, so I would like to have this output:
> > > >
> > > > <ead>
> > > > <banana/>
> > > > <archdesc>
> > > > <dsc>
> > > > <c00/>
> > > > <SELECT>
> > > > <c01/>
> > > > </SELECT>
> > > > <c02/>
> > > > </dsc>
> > > > </archdesc>
> > > > <custard/>
> > > > </ead>
> > >
> > > In XSLT this is simply an identity template that copies all elements
> > > unchanged:
> > >
> > > <xsl:template match="*">
> > > <xsl:copy><xsl:apply-templates/></xsl:copy>
> > > </xsl:template>
> > >
> > > (or a variation of that if you need to handle attributes)
> > >
> > > supplemented by a template for the element you want to change:
> > >
> > > <xsl:template match="c01">
> > > <SELECT><xsl:next-match/></SELECT>
> > > </xsl:template>
> > >
> > > (You can write a more elaborate match pattern if you need to be more
> > > selective.)
> > >
> > > In XQuery you need to program the recursive descent by hand:
> > >
> > > declare function local:processNode($e as node()) as element() {
> > > typeswitch ($e) {
> > > case element(c01) return <SELECT>{$e}</SELECT>
> > > case element() return
> > > element {node-name($e)} {for $c in $e/child::node() return
> > > local:processNode($c)}
> > > default return $e
> > > }
> > > }
> > >
> > > local:processNode(doc('abc.xml')/*)
> > >
> > > plus a bit of extra logic if you need to handle attributes
> > or namespaces.
> > >
> >
> > Ok, this is what I am using:
> >
> > declare function local:processNode($e as node()) as element()
> > { typeswitch ($e)
> > case element(c01)
> > return <SELECT>{$e}</SELECT>
> > case element()
> > return
> > element {node-name($e)} {
> > for $c in $e/child::node() return
> > local:processNode($c)
> > }
> > default return $e
> > };
> >
> > local:processNode(doc('ead_10748500.xml')/*)
> >
> > ...but I am getting an error when using that function:
> >
> > XPTY0004: Required item type of result of function
> > local:processNode() is element();
> > supplied value has item type text()
> > Query processing failed: Run-time errors were reported
> >
> > The error message is obvious, but probably typecasting won't
> > work here.
> >
> > Do you have any ideas?
> >
> > jz
> >
> >
> > > Michael Kay
> > > http://www.saxonica.com/
> > >
> > >
> > >
>
>
More information about the talk
mailing list