another XSLT problem

Discussion in 'XML' started by ned786, Aug 24, 2005.

  1. ned786

    ned786 Guest

    Hello,

    I'm trying to solve an XSLT problem, and I'm hoping someone can give a
    little guidance. I am transforming XML to HTML.

    Here's an example of the XML file I'm dealing with, greatly simplified:

    -----------------
    <root>

    <object name="a">
    <item name="a1">Text here</item>
    <item name="readonlya2">Text here</item>
    <item name="a3">Text here</item>
    <item name="readonlya4">Text here</item>
    <item name="a5">Text here</item>
    </object>

    <object name="b">
    <item name="b1">Text here</item>
    <item name="b2">Text here</item>
    <item name="b3">Text here</item>
    <item name="b4">Text here</item>
    <item name="b5">Text here</item>
    </object>

    </root>
    ----------------------

    An <object> element contains several <item> elements. I handle each
    <object> separately.

    If an <object> contains no read-only <item> elements (identified by
    name="readonly.."), I want to print "None" in the HTML output.

    If there is one or more read-only <item> in an <object>, I will display
    those read-only <item> elements in an HTML table.

    It seemed simple, but I have tried using a key, and a recursive
    template, and anything else I could think of. I can't find out if there
    are any read-only <item>s before putting something in the result tree.
    If I could set a global variable from within a template, then it would
    be easy to check that variable to tell whether or not to create a table
    or print "None." But that is apparently not possible.

    Have I missed something?

    Thanks!
    Mark
    ned786, Aug 24, 2005
    #1
    1. Advertising

  2. "ned786" <> wrote in message
    news:...
    > Hello,
    >
    > I'm trying to solve an XSLT problem, and I'm hoping someone can give a
    > little guidance. I am transforming XML to HTML.
    >
    > Here's an example of the XML file I'm dealing with, greatly simplified:
    >
    > -----------------
    > <root>
    >
    > <object name="a">
    > <item name="a1">Text here</item>
    > <item name="readonlya2">Text here</item>
    > <item name="a3">Text here</item>
    > <item name="readonlya4">Text here</item>
    > <item name="a5">Text here</item>
    > </object>
    >
    > <object name="b">
    > <item name="b1">Text here</item>
    > <item name="b2">Text here</item>
    > <item name="b3">Text here</item>
    > <item name="b4">Text here</item>
    > <item name="b5">Text here</item>
    > </object>
    >
    > </root>
    > ----------------------
    >
    > An <object> element contains several <item> elements. I handle each
    > <object> separately.
    >
    > If an <object> contains no read-only <item> elements (identified by
    > name="readonly.."), I want to print "None" in the HTML output.
    >
    > If there is one or more read-only <item> in an <object>, I will display
    > those read-only <item> elements in an HTML table.
    >
    > It seemed simple, but I have tried using a key, and a recursive
    > template, and anything else I could think of. I can't find out if there
    > are any read-only <item>s before putting something in the result tree.
    > If I could set a global variable from within a template, then it would
    > be easy to check that variable to tell whether or not to create a table
    > or print "None." But that is apparently not possible.


    Use:

    <xsl:stylesheet version="1.0"
    xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    <xsl:eek:utput method="html"/>

    <xsl:template match="object">
    <xsl:choose>
    <xsl:when test="not(*[starts-with(@name,'readonly')])">
    None
    </xsl:when>
    <xsl:eek:therwise>
    <table>
    <xsl:for-each select="*[starts-with(@name,'readonly')]">
    <tr><td><xsl:value-of select="@name"/></td></tr>
    </xsl:for-each>
    </table>
    </xsl:eek:therwise>
    </xsl:choose>
    </xsl:template>
    </xsl:stylesheet>
    Dimitre Novatchev, Aug 24, 2005
    #2
    1. Advertising

  3. ned786

    ned786 Guest

    Thank you! I will check this out. It worked on my simplified XML file,
    I will see what I get on the real thing.

    I didn't know you could put a predicate [...] on an asterisk (*), as in
    *[starts-with...etc].

    Mark
    ned786, Aug 24, 2005
    #3
  4. ned786

    Peter Flynn Guest

    ned786 wrote:

    > Hello,
    >
    > I'm trying to solve an XSLT problem, and I'm hoping someone can give a
    > little guidance. I am transforming XML to HTML.
    >
    > Here's an example of the XML file I'm dealing with, greatly simplified:
    >
    > -----------------
    > <root>
    >
    > <object name="a">
    > <item name="a1">Text here</item>
    > <item name="readonlya2">Text here</item>
    > <item name="a3">Text here</item>
    > <item name="readonlya4">Text here</item>
    > <item name="a5">Text here</item>
    > </object>
    >
    > <object name="b">
    > <item name="b1">Text here</item>
    > <item name="b2">Text here</item>
    > <item name="b3">Text here</item>
    > <item name="b4">Text here</item>
    > <item name="b5">Text here</item>
    > </object>
    >
    > </root>
    > ----------------------
    >
    > An <object> element contains several <item> elements. I handle each
    > <object> separately.
    >
    > If an <object> contains no read-only <item> elements (identified by
    > name="readonly.."), I want to print "None" in the HTML output.
    >
    > If there is one or more read-only <item> in an <object>, I will display
    > those read-only <item> elements in an HTML table.
    >
    > It seemed simple, but I have tried using a key, and a recursive
    > template, and anything else I could think of. I can't find out if there
    > are any read-only <item>s before putting something in the result tree.


    <xsl:template match="object">
    <xsl:variable name="roi"
    select="count(item[starts-with(@name,'readonly')])"/>
    <xsl:choose>
    <xsl:when test="$roi=0">
    <xsl:text>None</xsl:text>
    </xsl:when>
    <xsl:eek:therwise>
    ...do some table stuff...
    </xsl:eek:therwise>
    </xsl:choose>
    </xsl:template>

    > If I could set a global variable from within a template, then it would
    > be easy to check that variable to tell whether or not to create a table
    > or print "None." But that is apparently not possible.
    >
    > Have I missed something?


    Set a variable. Test it. Keep it simple :)
    The trick is getting to grips with what XPath can do.

    ///Peter
    Peter Flynn, Aug 24, 2005
    #4
  5. ned786

    Soren Kuula Guest

    ned786 wrote:
    > I didn't know you could put a predicate [...] on an asterisk (*), as in
    > *[starts-with...etc].


    That's not the beginning of the predicate; it's the end of the selection
    path (and in this case all of the selection path) before it.

    Soren
    Soren Kuula, Aug 25, 2005
    #5
  6. ned786

    ned786 Guest

    Peter,

    Wow, that fixes the problem, and it's elegant to boot! Thanks!

    Here's the way I had to modify it for my actual XML files:

    <xsl:variable name="ro"
    select="count(items/item[starts-with(@iname,'dr') or
    starts-with(@iname,'hr') or
    starts-with(@iname,'usr') or
    starts-with(@iname,'szr') or
    starts-with(@iname,'ulr') or
    starts-with(@iname,'ipr') or
    starts-with(@iname,'br') or
    starts-with(@iname,'ucr')])"/>
    <xsl:choose>
    <xsl:when test="$ro=0" >
    <p>No read-only items.</p>
    </xsl:when>
    <xsl:eek:therwise>
    <table>... etc.
    </table>
    </xsl:eek:therwise>
    </xsl:choose>

    As you can see, there are actually 8 different strings that indicate
    "read-only" (dr, hr, etc.). Now I am trying to figure out how to "NOT"
    that count() function above so I can do the same thing for the
    remaining writable items: Print "None" if there are none, and put them
    in a table if they exist. The following two have failed:

    <xsl:variable name="ro"
    select="count(items/item[not(starts-with(@iname,'dr')) or
    not(starts-with(@iname,'hr')) or
    ... ])"/>

    <xsl:variable name="ro"
    select="count(items/item[@iname != starts-with(@iname,'dr') or
    @iname != starts-with(@iname,'hr') or

    ... ])"/>


    I'm trying to find the <item> elements that do not have the read-only
    indication. If I can get this to work (find NOT read-only), it will
    save work. Do you have any further enlightenment?

    Regards,
    Mark
    ned786, Aug 26, 2005
    #6
  7. ned786

    Peter Flynn Guest

    ned786 wrote:

    > Peter,
    >
    > Wow, that fixes the problem, and it's elegant to boot! Thanks!
    >
    > Here's the way I had to modify it for my actual XML files:
    >
    > <xsl:variable name="ro"
    > select="count(items/item[starts-with(@iname,'dr') or
    > starts-with(@iname,'hr') or
    > starts-with(@iname,'usr') or
    > starts-with(@iname,'szr') or
    > starts-with(@iname,'ulr') or
    > starts-with(@iname,'ipr') or
    > starts-with(@iname,'br') or
    > starts-with(@iname,'ucr')])"/>
    > <xsl:choose>
    > <xsl:when test="$ro=0" >
    > <p>No read-only items.</p>
    > </xsl:when>
    > <xsl:eek:therwise>
    > <table>... etc.
    > </table>
    > </xsl:eek:therwise>
    > </xsl:choose>
    >
    > As you can see, there are actually 8 different strings that indicate
    > "read-only" (dr, hr, etc.). Now I am trying to figure out how to "NOT"
    > that count() function above so I can do the same thing for the
    > remaining writable items: Print "None" if there are none, and put them
    > in a table if they exist. The following two have failed:
    >
    > <xsl:variable name="ro"
    > select="count(items/item[not(starts-with(@iname,'dr')) or
    > not(starts-with(@iname,'hr')) or
    > ... ])"/>


    The above should work if you change all "or" to "and".
    You're negating the condition, so you must negate the conjunction too.

    ///Peter

    > <xsl:variable name="ro"
    > select="count(items/item[@iname != starts-with(@iname,'dr') or
    > @iname != starts-with(@iname,'hr') or
    >
    > ... ])"/>
    >
    >
    > I'm trying to find the <item> elements that do not have the read-only
    > indication. If I can get this to work (find NOT read-only), it will
    > save work. Do you have any further enlightenment?
    >
    > Regards,
    > Mark
    Peter Flynn, Aug 26, 2005
    #7
  8. ned786

    ned786 Guest

    Your last suggestion also works, Peter, and you are my hero. Thanks!
    The problem is fixed and it saves me a tedious maintenance chore.

    If you're interested, I had to add in yet another check to make it all
    work, and the final gnarly XPath looks like this:

    <xsl:variable name="wri"
    select="count(items/item[(not(@doc = 'no') and @iname !=
    starts-with(@iname,'dr')) and
    (not(@doc = 'no') and @iname != starts-with(@iname,'hr')) and
    (not(@doc = 'no') and @iname != starts-with(@iname,'usr')) and
    (not(@doc = 'no') and @iname != starts-with(@iname,'szr')) and
    (not(@doc = 'no') and @iname != starts-with(@iname,'ulr')) and
    (not(@doc = 'no') and @iname != starts-with(@iname,'ipr')) and
    (not(@doc = 'no') and @iname != starts-with(@iname,'br')) and
    (not(@doc = 'no') and @iname != starts-with(@iname,'ucr'))])"/>

    Weirdly, "not(@doc = no)" worked when "@doc != 'no'" did not. But I'm
    happy.

    Mark
    ned786, Aug 31, 2005
    #8
  9. ned786

    Peter Flynn Guest

    ned786 wrote:

    > Your last suggestion also works, Peter, and you are my hero. Thanks!
    > The problem is fixed and it saves me a tedious maintenance chore.


    My pleasure...

    > If you're interested, I had to add in yet another check to make it all
    > work, and the final gnarly XPath looks like this:
    >
    > <xsl:variable name="wri"
    > select="count(items/item[(not(@doc = 'no') and @iname !=
    > starts-with(@iname,'dr')) and
    > (not(@doc = 'no') and @iname != starts-with(@iname,'hr')) and
    > (not(@doc = 'no') and @iname != starts-with(@iname,'usr')) and
    > (not(@doc = 'no') and @iname != starts-with(@iname,'szr')) and
    > (not(@doc = 'no') and @iname != starts-with(@iname,'ulr')) and
    > (not(@doc = 'no') and @iname != starts-with(@iname,'ipr')) and
    > (not(@doc = 'no') and @iname != starts-with(@iname,'br')) and
    > (not(@doc = 'no') and @iname != starts-with(@iname,'ucr'))])"/>


    I'm not clear what this is trying to achieve. @iname is an attribute:
    testing its inequality against a boolean like starts-with will probably
    have unexpected effects, depending on whether the attribute is present
    or not. If present, and starting with 'hr', then the first test should
    always evaluate false.

    > Weirdly, "not(@doc = no)" worked when "@doc != 'no'" did not. But I'm
    > happy.


    I have the feeling I've seen this too...possibly not all processors are
    happy with negated boolean conditions involving attributes.

    ///Peter
    Peter Flynn, Sep 3, 2005
    #9
  10. ned786

    ned786 Guest

    Peter Flynn wrote:
    > > <xsl:variable name="wri"
    > > select="count(items/item[(not(@doc = 'no') and @iname !=
    > > starts-with(@iname,'dr')) and
    > > (not(@doc = 'no') and @iname != starts-with(@iname,'hr')) and
    > > (not(@doc = 'no') and @iname != starts-with(@iname,'usr')) and
    > > (not(@doc = 'no') and @iname != starts-with(@iname,'szr')) and
    > > (not(@doc = 'no') and @iname != starts-with(@iname,'ulr')) and
    > > (not(@doc = 'no') and @iname != starts-with(@iname,'ipr')) and
    > > (not(@doc = 'no') and @iname != starts-with(@iname,'br')) and
    > > (not(@doc = 'no') and @iname != starts-with(@iname,'ucr'))])"/>

    >
    > I'm not clear what this is trying to achieve. @iname is an attribute:
    > testing its inequality against a boolean like starts-with will probably
    > have unexpected effects, depending on whether the attribute is present
    > or not. If present, and starting with 'hr', then the first test should
    > always evaluate false.


    Here's what this achieves. This XPath finds all the writable <item>
    elements for me, because I can identify them when the iname attribute
    does NOT start with one of the read-only strings (hr, usr, ulr, etc.).

    If you are questioning the syntax of saying "@iname !=
    starts-with(@iname,...)" instead of just "!= starts-with(@iname,...)",
    I did it that way because it seemed I had to or it didn't work. When I
    remove the "@iname !=", I get the error "Unexpected token != in
    expression" and it stops the transform. (I'm using Saxon at the command
    line.)

    I also tried putting the expression in parentheses like (!=
    starts-with(@iname,...)), and the same error occurred. It only worked
    when I put "@iname != starts-with(@iname,...)".

    At this point I think I will just take the money and run. I can count
    on there always being an iname attribute.

    Thanks again,
    Mark
    ned786, Sep 6, 2005
    #10
  11. ned786

    Peter Flynn Guest

    ned786 wrote:
    >> > <xsl:variable name="wri"
    >> > select="count(items/item[(not(@doc = 'no') and @iname !=
    >> > starts-with(@iname,'dr')) and
    >> > (not(@doc = 'no') and @iname != starts-with(@iname,'hr')) and
    >> > (not(@doc = 'no') and @iname != starts-with(@iname,'usr')) and
    >> > (not(@doc = 'no') and @iname != starts-with(@iname,'szr')) and
    >> > (not(@doc = 'no') and @iname != starts-with(@iname,'ulr')) and
    >> > (not(@doc = 'no') and @iname != starts-with(@iname,'ipr')) and
    >> > (not(@doc = 'no') and @iname != starts-with(@iname,'br')) and
    >> > (not(@doc = 'no') and @iname != starts-with(@iname,'ucr'))])"/>

    >>
    >> I'm not clear what this is trying to achieve. @iname is an attribute:
    >> testing its inequality against a boolean like starts-with will probably
    >> have unexpected effects, depending on whether the attribute is present
    >> or not. If present, and starting with 'hr', then the first test should
    >> always evaluate false.

    >
    > Here's what this achieves. This XPath finds all the writable <item>
    > elements for me, because I can identify them when the iname attribute
    > does NOT start with one of the read-only strings (hr, usr, ulr, etc.).
    >
    > If you are questioning the syntax of saying "@iname !=
    > starts-with(@iname,...)" instead of just "!= starts-with(@iname,...)",
    > I did it that way because it seemed I had to or it didn't work. When I
    > remove the "@iname !=", I get the error "Unexpected token != in
    > expression" and it stops the transform. (I'm using Saxon at the command
    > line.)


    It's the = sign I'm questioning. What about !starts-with or
    not(starts-with(...))

    I am reminded of the programming language built into the P-Stat stats
    package (PPL) which contained the amazingly useful syntax

    if any(varname,varname,varname,...) among (value,value,value,...)

    :)

    ///Peter
    Peter Flynn, Sep 7, 2005
    #11
  12. ned786

    ned786 Guest

    I tried out what you suggest.

    This works:
    (not(@doc = 'no') and not(starts-with(@iname,'dr')))

    This doesn't work and stops the transform:
    (not(@doc = 'no') and !starts-with(@iname,'dr'))

    with this error:
    "!" without "=" in expression count(...etc.

    Your "not(starts-with..." makes the XPath simpler and easier for
    someone to understand, so I'll use it.

    Thanks yet again!
    Mark
    ned786, Sep 8, 2005
    #12
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Stylus Studio
    Replies:
    0
    Views:
    637
    Stylus Studio
    Aug 3, 2004
  2. Benjamin Hillsley
    Replies:
    3
    Views:
    1,661
    Dimitre Novatchev
    Sep 25, 2003
  3. ted
    Replies:
    1
    Views:
    613
    Laurens
    Jan 26, 2004
  4. Replies:
    2
    Views:
    708
    Henry S. Thompson
    Oct 19, 2005
  5. Replies:
    18
    Views:
    2,743
    Joseph Kesselman
    Oct 4, 2006
Loading...

Share This Page