Hello and welcome to our community! Is this your first visit?
Register
Enjoy an ad free experience by logging in. Not a member yet? Register.
Results 1 to 13 of 13
  1. #1
    Moderator
    Join Date
    May 2002
    Location
    Hayward, CA
    Posts
    1,461
    Thanks
    1
    Thanked 23 Times in 21 Posts

    XSLT to emulate DOM Node.cloneNode(true)?

    I've been thinking about how nice the cloneNode() method of the Document Object Model is, and the fact that SVG has the <use /> element which essentially does the same thing.

    So I'm thinking it's high time I asked how to create a similar effect in XSLT. (XML documents typically aren't scripted, according to one person who disagrees with me...)

    That way, I can just have an element:


    <html:p id="Hello">Hello World</html:p>
    <jsl:clone xlink:href="#Hello" />

    And the XSLT processor would output:

    <html:p id="Hello">Hello World</html:p>
    <html:p>Hello World</html:p>

    Ideas, anyone?
    "The first step to confirming there is a bug in someone else's work is confirming there are no bugs in your own."
    June 30, 2001
    author, Verbosio prototype XML Editor
    author, JavaScript Developer's Dictionary
    https://alexvincent.us/blog

  • #2
    jkd
    jkd is offline
    Senior Coder jkd's Avatar
    Join Date
    May 2002
    Location
    metro DC
    Posts
    3,163
    Thanks
    1
    Thanked 18 Times in 18 Posts
    ... You could probably take a substr(1) from that xlink:href attribute, and match it against an attribute selector...

    I might post something a little later to that effect.

  • #3
    jkd
    jkd is offline
    Senior Coder jkd's Avatar
    Join Date
    May 2002
    Location
    metro DC
    Posts
    3,163
    Thanks
    1
    Thanked 18 Times in 18 Posts
    <xsl:template match="//clone">
    <xsl:copy-of select="id(substring(@href, 2))"/>
    </xsl:template>

    This is completely ignoring namespaces. To be honest, I don't think I recall how XPath deals with namespaces.... someone with some knowledge want to clear it up?

  • #4
    jkd
    jkd is offline
    Senior Coder jkd's Avatar
    Join Date
    May 2002
    Location
    metro DC
    Posts
    3,163
    Thanks
    1
    Thanked 18 Times in 18 Posts
    Conversation between Alex and I over this (useless parts edited out) in case anyone is interested (some interesting stuff if you want to read through the poorly copied text from the IRC chat):

    ..........
    jasonkarldavis I don't know if XPath respects namespaces when matching attributes and tags or not
    jasonkarldavis but the code itself it surprisingly simple
    jasonkarldavis I thought it was gonna be way more complicated
    WeirdAl well I was primarily concerned about matching by ID
    jasonkarldavis but XPath has the nice id() function.... :)
    jasonkarldavis which I didn't know about until 3 minutes ago
    WeirdAl I figured it'd be simple
    WeirdAl I just don't mess with XSLT ;)
    jasonkarldavis I figure it is something worth knowing well... so I try to spend time with it now and then to keep fresh
    WeirdAl :) -- well, as I said, it's somthing I figured you'd be the one to nail
    WeirdAl goes to pull up XPath 1.0
    WeirdAl -- I thought you'd use XPointer though
    jasonkarldavis why?
    jasonkarldavis XPointer is just like anchors for XML...
    jasonkarldavis you can't actually grab content with it
    WeirdAl http://www.w3.org/TR/xpath
    WeirdAl :) I can't exactly tell the diff
    jasonkarldavis picture XPointer as a replacement for <a name="myanchor">
    WeirdAl http://www.w3.org/TR/xpath#namespace-nodes
    jasonkarldavis recommends /XML in a Nutshell/ by O'Reilly
    jasonkarldavis best reference I've ever gotten
    .........
    jasonkarldavis but XML In a Nutshell is *nice*
    jasonkarldavis hehe
    WeirdAl anyway check out that link I just gave you and tell me if it helps
    jasonkarldavis that's not *quite* what I was looking for
    WeirdAl try the expanded-names link
    jasonkarldavis namespace nodes are one of the things Mozilla's Transformiix doesn't support
    WeirdAl :(
    WeirdAl section 4.1 of the same doc, what about that?
    jasonkarldavis that expanded names thing is what I wanted I think
    jasonkarldavis :)
    WeirdAl if Moz supports it...
    WeirdAl when in doubt, go get the spec
    WeirdAl oh, and can you please make sure that the copy itself doesn't have any id attributes?
    WeirdAl -- or better yet, lets me set a new id attribute?
    WeirdAl in the original element
    WeirdAl for the top element being copied?
    WeirdAl -- that would be a killer XSLT app
    jasonkarldavis umm
    jasonkarldavis hmm
    WeirdAl :) too much to swallow?
    jasonkarldavis :: thinking ::
    jasonkarldavis <xsl:attribute/> would be beautiful... but copied node isn't the "context node"
    WeirdAl ?
    jasonkarldavis yeah... I'm trying to think of how to access the copied fragment tree
    WeirdAl -- maybe a *second* XSLT stylesheet
    WeirdAl -- applied after the first!
    jasonkarldavis does XSLT operate on itself? Like, once it figures out how to insert content, does it do it all at once?
    jasonkarldavis or does it do it in order?
    WeirdAl *shrug* I dunno
    jasonkarldavis if it is in order... then you could just find the first-child of //clone
    WeirdAl -- but that could be a text node or null
    jasonkarldavis well, first element node
    jasonkarldavis (and only)
    WeirdAl :)
    WeirdAl -- would it be guaranteed to be the intended node?
    jasonkarldavis you can't select document fragments until XPointer works... therefore you are guarenteed that the <xsl:copy-of select="blabla"/> is an element node
    jasonkarldavis besides, id() in this case only can return a single element node
    WeirdAl -- in theory
    jasonkarldavis as only elements can have id's
    WeirdAl ah, but then we run into docs with two elems sharing the same id
    jasonkarldavis which are invalid HTML/XHTML, but still well-formed XML....
    jasonkarldavis id() can take multiple arguments
    jasonkarldavis and will return a node-set of all matching nodes
    WeirdAl -- hence why I'm trying to make sure we keep cloned id's out
    jasonkarldavis or if multiple elements have the same id(), it returns a node-et of them
    WeirdAl XSLT 1.0, Section 5.7: Modes allow an element to be processed multiple times, each time producing a different result.
    jasonkarldavis does this reprocessing also pick up on previously inserted XSLT-generated templates?
    WeirdAl read it
    WeirdAl seems to
    jasonkarldavis well then, create another template after this one
    WeirdAl not me :)
    jasonkarldavis is this my project now? ;) :D
    jasonkarldavis j/k
    WeirdAl -- I'm trying to figure out if XSLT lets us remove things instead of just creating them
    jasonkarldavis I can "reset" the id attribute
    WeirdAl :) that'd be cool -- if you reset them all to "" for the copy
    jasonkarldavis <xsl:template match="//clone/*[position() = 1]">
    jasonkarldavis <xsl:attribute name="id"/>
    WeirdAl ?
    jasonkarldavis </xsl:template>
    WeirdAl hm
    jasonkarldavis the content of <attribute/> is the new attribute value
    jasonkarldavis in this case, I don't put anything in
    WeirdAl ahhhhhhhhh
    jasonkarldavis ? :)
    WeirdAl that makes life interesting -- especially if you use a conditional to determine if it's the element actually being cloned
    WeirdAl -- I think
    jasonkarldavis well, we are assuming that the only children of <clone/> are what we put there via XSLT
    jasonkarldavis which we become sure of it just a single element
    WeirdAl which for the moment they would be
    jasonkarldavis unless we have multiple same id;'s
    WeirdAl uh oh... :)
    jasonkarldavis which probably implies we are inserting content at a sibling level as a child of <clone/>?
    WeirdAl hm -- yeah, that would be the safe bet
    jasonkarldavis instead of only having firstChild, we have childNodes
    WeirdAl oh, wait
    jasonkarldavis which then makes:
    jasonkarldavis match="//clone/*[position() = 1]"
    jasonkarldavis become:
    WeirdAl -- what I don't get is why we can't just replace <jsl:clone />
    jasonkarldavis match="//clone/*"
    jasonkarldavis simply replace the node?
    WeirdAl exactly
    WeirdAl that's safer imho
    jasonkarldavis it would be easier to keep everything as children of it
    jasonkarldavis plus, this way is more in line with the SVG spec
    jasonkarldavis which you mentioned
    WeirdAl -- does use create childnodes?
    jasonkarldavis <use/> basically inserts almost anonymous content
    jasonkarldavis the DOM only sees <use/>, but you can access its content through a property
    WeirdAl heh, I'm rusty on the SVG DOM :)
    jasonkarldavis UseElement.hasChildNodes() should be false
    (Edited by Alex to remove a few slightly improper words that should not be posted here)
    (Because Alex is a naughty moderator. :p - jkd )
    Last edited by jkd; 10-22-2002 at 05:18 AM.

  • #5
    jkd
    jkd is offline
    Senior Coder jkd's Avatar
    Join Date
    May 2002
    Location
    metro DC
    Posts
    3,163
    Thanks
    1
    Thanked 18 Times in 18 Posts
    Some food for thought:

    Code:
    <xsl:template match="//clone">
    	<xsl:for-each select="id(substring(@href, 2))">
    		<xsl:copy-of select="."/>
    	</xsl:for-each>
    </xsl:template>
    
    <xsl:template match="//clone/*">
    	<xsl:attribute name="id"/>
    </xsl:template>
    That should resolve multiple-same id problems.

    I am still unsure of whether or not the second template will do anything though, but I intend to start testing it sometime later today...

  • #6
    jkd
    jkd is offline
    Senior Coder jkd's Avatar
    Join Date
    May 2002
    Location
    metro DC
    Posts
    3,163
    Thanks
    1
    Thanked 18 Times in 18 Posts
    Here is what I'm using:

    Code:
    <?xml version="1.0" encoding="UTF-8"?>
    <xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
    
    	<xsl:template match="//clone">
    		<clone>
    			<xsl:for-each select="id(substring(@href, 2))">
    				<xsl:copy-of select="."/>
    			</xsl:for-each>
    		</clone>
    	</xsl:template>
    	
    	<xsl:template match="//clone/*">
    		<xsl:attribute name="id"/>
    	</xsl:template>
    
    	<xsl:template match="/node()">
    		<xsl:copy-of select="."/>
    	</xsl:template>
    </xsl:stylesheet>
    And applying it to:
    Code:
    <?xml version="1.0"?>
    <?xml-stylesheet type="text/xsl" href="html.xsl"?>
    <html xmlns="http://www.w3.org/1999/xhtml" xmlns:xlink="http://www.w3.org/1999/xlink" xml:lang="en">
    	<head>
    		<title>XSLT clone test</title>
    	</head>
    	<body>
    		<div id="reuse">cloned content?</div>
    		<clone xlink:type="simple" xlink:href="#reuse"/>
    		<hr/>
    		<clone xlink:type="simple" xlink:href="#reuse"/>
    	</body>
    </html>
    Doesn't result in *any* change to the document structure.

  • #7
    jkd
    jkd is offline
    Senior Coder jkd's Avatar
    Join Date
    May 2002
    Location
    metro DC
    Posts
    3,163
    Thanks
    1
    Thanked 18 Times in 18 Posts
    I think something is conceptually wrong with our approach... I don't know how to voice it clearly, but we need to:

    1. Copy the entire document
    2. While copying, replace <clone/> with <clone><!-- inserted content --></clone>

    But to copy a document and maintain its original structure, while also looking for <clone/>, we are basically going to need to implement a DOM2 Traversal TreeWalker in XSLT?

  • #8
    Moderator
    Join Date
    May 2002
    Location
    Hayward, CA
    Posts
    1,461
    Thanks
    1
    Thanked 23 Times in 21 Posts
    I don't think Mozilla supports what we're looking for directly. There was a FAQ I read which showed how to copy all elements but one, and it didn't work in Mozilla. Looks like I'll have to use DOM.
    "The first step to confirming there is a bug in someone else's work is confirming there are no bugs in your own."
    June 30, 2001
    author, Verbosio prototype XML Editor
    author, JavaScript Developer's Dictionary
    https://alexvincent.us/blog

  • #9
    Moderator
    Join Date
    May 2002
    Location
    Hayward, CA
    Posts
    1,461
    Thanks
    1
    Thanked 23 Times in 21 Posts
    <xsl:template match="@*|node()"><xsl:copy><xsl:apply-templates select="@*|node()"/></xsl:copy></xsl:template><xsl:template match="edit:remove" xmlns:edit="http://foo.org/bar"/>

    This is courtesy of Jonas Sicking, mozilla.org volunteer.

    For removing a node.

    Resurrecting dead threads is fun, particularly if a partial solution has been found.
    Last edited by Alex Vincent; 12-15-2002 at 03:34 AM.
    "The first step to confirming there is a bug in someone else's work is confirming there are no bugs in your own."
    June 30, 2001
    author, Verbosio prototype XML Editor
    author, JavaScript Developer's Dictionary
    https://alexvincent.us/blog

  • #10
    jkd
    jkd is offline
    Senior Coder jkd's Avatar
    Join Date
    May 2002
    Location
    metro DC
    Posts
    3,163
    Thanks
    1
    Thanked 18 Times in 18 Posts
    Ok, so this removes a node... call me narrow, but I'm trying to wrap my head around how it will help us out...

  • #11
    Moderator
    Join Date
    May 2002
    Location
    Hayward, CA
    Posts
    1,461
    Thanks
    1
    Thanked 23 Times in 21 Posts
    Well, if we can insert an XSLT element that will copy the targeted node as a child of the second template element, we should be in business.
    "The first step to confirming there is a bug in someone else's work is confirming there are no bugs in your own."
    June 30, 2001
    author, Verbosio prototype XML Editor
    author, JavaScript Developer's Dictionary
    https://alexvincent.us/blog

  • #12
    jkd
    jkd is offline
    Senior Coder jkd's Avatar
    Join Date
    May 2002
    Location
    metro DC
    Posts
    3,163
    Thanks
    1
    Thanked 18 Times in 18 Posts
    So I'm an awful thread digger, but I recently needed this capability and finally solved the problem:

    identity.xml:
    Code:
    <?xml version="1.0"?>
    <stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform">
    	<template match="node()|@*">
    		<copy><apply-templates select="node()|@*"/></copy>
    	</template>
    </stylesheet>
    cloner.xml:
    Code:
    <stylesheet version="1.0" xmlns="http://www.w3.org/1999/XSL/Transform" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:jkd="http://www.jasonkarldavis.com/xml">
    	<import href="identity.xml"/>
    
    	<output method="xml"
    	        indent="yes"
    	        omit-xml-declaration="no"
    	        media-type="application/xhtml+xml"
    	        doctype-public="-//W3C//DTD XHTML 1.1//EN"
    	        doctype-system="http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd"
    	/>
    
    	<template match="jkd:clone">
    		<apply-templates select="id(substring(@xlink:href, 2))" mode="clone"/>
    	</template>
    	<template match="*" mode="clone">
    		<copy><apply-templates select="node()|@*[name()!='id']"/></copy>
    	</template>
    </stylesheet>
    And of course, the sample document:
    Code:
    <?xml version="1.0"?>
    <?xml-stylesheet type="text/xsl" href="cloner.xml"?>
    <html xmlns="http://www.w3.org/1999/xhtml" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:jkd="http://www.jasonkarldavis.com/xml" xml:lang="en">
    	<head>
    		<title>XSLT clone test</title>
    	</head>
    	<body>
    		<div id="reuse">cloned content?</div>
    		<jkd:clone xlink:type="simple" xlink:href="#reuse"/>
    		<hr/>
    		<jkd:clone xlink:type="simple" xlink:href="#reuse"/>
    	</body>
    </html>
    Which outputs:
    Code:
    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
    <?xml-stylesheet type="text/xsl" href="clone.xml"?>
    <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
    	<head>
    		<title>XSLT clone test</title>
    	</head>
    	<body>
    		<div id="reuse">cloned content?</div>
    		<div>cloned content?</div>
    		<hr/>
    		<div>cloned content?</div>
    	</body>
    </html>

  • #13
    Regular Coder
    Join Date
    Oct 2003
    Location
    London, UK
    Posts
    411
    Thanks
    0
    Thanked 1 Time in 1 Post
    Rather interesting!
    Marcus Tucker / www / blog
    Web Analyst Programmer / Voted SPF "ASP Guru"


  •  

    Posting Permissions

    • You may not post new threads
    • You may not post replies
    • You may not post attachments
    • You may not edit your posts
    •