Quantcast

Nested templates / multi-result documents

classic Classic list List threaded Threaded
4 messages Options
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Nested templates / multi-result documents

Chris Wolf
Hello,

I have an xml document with a repeating data structure of nested
elements, one level deep.  I would like a result document to accumulate
the repeating outer element data and another result document to
accumulate the the repeating nested element data.  Is this even
possible?  I am attempting to transform the XML into two csv files where
one file has the outer content and another file has the inner content.

My attempts always result in this error:  "Cannot write more than one
result document to the same URI:"

Thanks for any assistance....

Chris Wolf

e.g.:

<root>
   <a id="1">
     <foo>fooval1</foo>
     <bar/>
     <b id="1">
       <frizzle>frizzle-1</frizzle>
       <fargle/>
     </b>
   </a>
   <a id="2">
     <foo>fooval2</foo>
     <bar/>
     <b id="1">
       <frizzle>frizzle-2</frizzle>
       <fargle/>
     </b>
   </a>
</root>



<?xml version="1.0"?>
<xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
     <xsl:output omit-xml-declaration="yes" method="text" name="text"/>
      <xsl:variable name="outer_fn">outer.txt</xsl:variable>
      <xsl:variable name="inner_fn">inner.txt</xsl:variable>

      <xsl:template match="//a">
        <xsl:result-document href="{$outer_fn}" format="text">
          <xsl:variable name="outerId"><xsl:value-of
select="@id"/></xsl:variable>
          <xsl:value-of select="$outerId"/><xsl:text>,</xsl:text>
          <xsl:value-of select="foo"/><xsl:text>
</xsl:text>
          <xsl:apply-templates select="b">
            <xsl:with-param name="oid" select="$outerId"/>
          </xsl:apply-templates>
        </xsl:result-document>
      </xsl:template>

      <xsl:template match="b">
        <xsl:param name="oid"/>
        <xsl:result-document href="{$inner_fn}" format="text">
          <xsl:value-of select="$oid"/><xsl:text>,</xsl:text>
          <xsl:value-of select="frizzle"/><xsl:text>
</xsl:text>
          </xsl:result-document>
      </xsl:template>

</xsl:transform>


------------------------------------------------------------------------------
_______________________________________________
saxon-help mailing list archived at http://saxon.markmail.org/
[hidden email]
https://lists.sourceforge.net/lists/listinfo/saxon-help 
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Nested templates / multi-result documents

Martin Honnen-2
On 30.08.2016 16:02, Chris Wolf wrote:

> I have an xml document with a repeating data structure of nested
> elements, one level deep.  I would like a result document to accumulate
> the repeating outer element data and another result document to
> accumulate the the repeating nested element data.  Is this even
> possible?  I am attempting to transform the XML into two csv files where
> one file has the outer content and another file has the inner content.
>
> My attempts always result in this error:  "Cannot write more than one
> result document to the same URI:"

As your sample has two "a" and two "b" elements obviously using
   <xsl:result-document href="{$outer_fn}" format="text">
inside the template matching "a" will try to write twice to the same
document.

So I suspect you want something along the lines of

   <xsl:template match="/*">
     <xsl:result-document href="{$outer_fn}" format="text">
       <xsl:apply-templates select="a"/>
     </xsl:result-document>
     <xsl:result-document href="{$inner_fn}" format="text">
       <xsl:apply-templates select="//b"/>
     </xsl:result-document>
   </xsl:template>

or perhaps separating the second processing of "a" through a mode

   <xsl:template match="/*">
     <xsl:result-document href="{$outer_fn}" format="text">
       <xsl:apply-templates select="a"/>
     </xsl:result-document>
     <xsl:result-document href="{$inner_fn}" format="text">
       <xsl:apply-templates select="a" mode="foo"/>
     </xsl:result-document>
   </xsl:template>

> <root>
>    <a id="1">
>      <foo>fooval1</foo>
>      <bar/>
>      <b id="1">
>        <frizzle>frizzle-1</frizzle>
>        <fargle/>
>      </b>
>    </a>
>    <a id="2">
>      <foo>fooval2</foo>
>      <bar/>
>      <b id="1">
>        <frizzle>frizzle-2</frizzle>
>        <fargle/>
>      </b>
>    </a>
> </root>
>
>
>
> <?xml version="1.0"?>
> <xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
> version="2.0">
>      <xsl:output omit-xml-declaration="yes" method="text" name="text"/>
>       <xsl:variable name="outer_fn">outer.txt</xsl:variable>
>       <xsl:variable name="inner_fn">inner.txt</xsl:variable>
>
>       <xsl:template match="//a">
>         <xsl:result-document href="{$outer_fn}" format="text">
>           <xsl:variable name="outerId"><xsl:value-of
> select="@id"/></xsl:variable>
>           <xsl:value-of select="$outerId"/><xsl:text>,</xsl:text>
>           <xsl:value-of select="foo"/><xsl:text>
> </xsl:text>
>           <xsl:apply-templates select="b">
>             <xsl:with-param name="oid" select="$outerId"/>
>           </xsl:apply-templates>
>         </xsl:result-document>
>       </xsl:template>
>
>       <xsl:template match="b">
>         <xsl:param name="oid"/>
>         <xsl:result-document href="{$inner_fn}" format="text">
>           <xsl:value-of select="$oid"/><xsl:text>,</xsl:text>
>           <xsl:value-of select="frizzle"/><xsl:text>
> </xsl:text>
>           </xsl:result-document>
>       </xsl:template>
>
> </xsl:transform>
>
>
> ------------------------------------------------------------------------------
> _______________________________________________
> saxon-help mailing list archived at http://saxon.markmail.org/
> [hidden email]
> https://lists.sourceforge.net/lists/listinfo/saxon-help
>


------------------------------------------------------------------------------
_______________________________________________
saxon-help mailing list archived at http://saxon.markmail.org/
[hidden email]
https://lists.sourceforge.net/lists/listinfo/saxon-help 
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Nested templates / multi-result documents

Chris Wolf
Hi Martin,

Thanks for your suggestions.   My example is contrived but represents
the actual problem in it's structure.

There are an arbitrarily large number of "a" (not just two) and each "a"
has an arbitrary number of "b" child nodes.  The relationship of child
"b" to parent "a" needs to be maintained - thus the second call to
"apply-templates select='b'" within "a"'s template and passing the a.id
as a template parameter.

Essentially, I'm trying to "unnest" the parents to one csv file and
children (marked with parent id attribute) to a second csv file.  I have
a feeling I might not be able to solve this with xslt... :(

Thanks,

Chris Wolf

On 8/30/2016 10:48 AM, Martin Honnen wrote:

> On 30.08.2016 16:02, Chris Wolf wrote:
>
>> I have an xml document with a repeating data structure of nested
>> elements, one level deep.  I would like a result document to accumulate
>> the repeating outer element data and another result document to
>> accumulate the the repeating nested element data.  Is this even
>> possible?  I am attempting to transform the XML into two csv files where
>> one file has the outer content and another file has the inner content.
>>
>> My attempts always result in this error:  "Cannot write more than one
>> result document to the same URI:"
> As your sample has two "a" and two "b" elements obviously using
>     <xsl:result-document href="{$outer_fn}" format="text">
> inside the template matching "a" will try to write twice to the same
> document.
>
> So I suspect you want something along the lines of
>
>     <xsl:template match="/*">
>       <xsl:result-document href="{$outer_fn}" format="text">
>         <xsl:apply-templates select="a"/>
>       </xsl:result-document>
>       <xsl:result-document href="{$inner_fn}" format="text">
>         <xsl:apply-templates select="//b"/>
>       </xsl:result-document>
>     </xsl:template>
>
> or perhaps separating the second processing of "a" through a mode
>
>     <xsl:template match="/*">
>       <xsl:result-document href="{$outer_fn}" format="text">
>         <xsl:apply-templates select="a"/>
>       </xsl:result-document>
>       <xsl:result-document href="{$inner_fn}" format="text">
>         <xsl:apply-templates select="a" mode="foo"/>
>       </xsl:result-document>
>     </xsl:template>
>> <root>
>>     <a id="1">
>>       <foo>fooval1</foo>
>>       <bar/>
>>       <b id="1">
>>         <frizzle>frizzle-1</frizzle>
>>         <fargle/>
>>       </b>
>>     </a>
>>     <a id="2">
>>       <foo>fooval2</foo>
>>       <bar/>
>>       <b id="1">
>>         <frizzle>frizzle-2</frizzle>
>>         <fargle/>
>>       </b>
>>     </a>
>> </root>
>>
>>
>>
>> <?xml version="1.0"?>
>> <xsl:transform xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
>> version="2.0">
>>       <xsl:output omit-xml-declaration="yes" method="text" name="text"/>
>>        <xsl:variable name="outer_fn">outer.txt</xsl:variable>
>>        <xsl:variable name="inner_fn">inner.txt</xsl:variable>
>>
>>        <xsl:template match="//a">
>>          <xsl:result-document href="{$outer_fn}" format="text">
>>            <xsl:variable name="outerId"><xsl:value-of
>> select="@id"/></xsl:variable>
>>            <xsl:value-of select="$outerId"/><xsl:text>,</xsl:text>
>>            <xsl:value-of select="foo"/><xsl:text>
>> </xsl:text>
>>            <xsl:apply-templates select="b">
>>              <xsl:with-param name="oid" select="$outerId"/>
>>            </xsl:apply-templates>
>>          </xsl:result-document>
>>        </xsl:template>
>>
>>        <xsl:template match="b">
>>          <xsl:param name="oid"/>
>>          <xsl:result-document href="{$inner_fn}" format="text">
>>            <xsl:value-of select="$oid"/><xsl:text>,</xsl:text>
>>            <xsl:value-of select="frizzle"/><xsl:text>
>> </xsl:text>
>>            </xsl:result-document>
>>        </xsl:template>
>>
>> </xsl:transform>
>>
>>
>> ------------------------------------------------------------------------------
>> _______________________________________________
>> saxon-help mailing list archived at http://saxon.markmail.org/
>> [hidden email]
>> https://lists.sourceforge.net/lists/listinfo/saxon-help
>>
>
> ------------------------------------------------------------------------------
> _______________________________________________
> saxon-help mailing list archived at http://saxon.markmail.org/
> [hidden email]
> https://lists.sourceforge.net/lists/listinfo/saxon-help


------------------------------------------------------------------------------
_______________________________________________
saxon-help mailing list archived at http://saxon.markmail.org/
[hidden email]
https://lists.sourceforge.net/lists/listinfo/saxon-help 
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Nested templates / multi-result documents

Martin Honnen-2
On 30.08.2016 19:25, Chris Wolf wrote:

> Thanks for your suggestions.   My example is contrived but represents
> the actual problem in it's structure.
>
> There are an arbitrarily large number of "a" (not just two) and each "a"
> has an arbitrary number of "b" child nodes.  The relationship of child
> "b" to parent "a" needs to be maintained - thus the second call to
> "apply-templates select='b'" within "a"'s template and passing the a.id
> as a template parameter.
>
> Essentially, I'm trying to "unnest" the parents to one csv file and
> children (marked with parent id attribute) to a second csv file.  I have
> a feeling I might not be able to solve this with xslt... :(


You can pass that value on or you can simply navigate up to the parent
to select it.

Thus if you use

    <xsl:template match="/*">
      <xsl:result-document href="{$outer_fn}" format="text">
        <xsl:apply-templates select="a"/>
      </xsl:result-document>
      <xsl:result-document href="{$inner_fn}" format="text">
        <xsl:apply-templates select="a" mode="inner"/>
      </xsl:result-document>
    </xsl:template>

    <xsl:template match="a">
      <xsl:value-of select="@id, foo" separator=","/>
      <xsl:text>&#10;</xsl:text>
    </xsl:template>

    <xsl:template match="a" mode="inner">
      <xsl:apply-templates select="b">
        <xsl:with-param name="oid" select="@id"/>
      </xsl:apply-templates>
    </xsl:template>

    <xsl:template match="b">
      <xsl:param name="oid"/>
      <xsl:value-of select="$oid, frizzle" separator=","/>
      <xsl:text>&10;</xsl:text>
    </xsl:template>

you can use the parameter and can pass it on.

The only restructuring you need is to make sure you use the
xsl:result-document with a certain href only once and then push the
nodes you want to output to its matching templates instead of using
xsl:result-document in the template itself.

------------------------------------------------------------------------------
_______________________________________________
saxon-help mailing list archived at http://saxon.markmail.org/
[hidden email]
https://lists.sourceforge.net/lists/listinfo/saxon-help 
Loading...