Subject: Re: [xsl] Stylesheet from a stylesheet From: Midsummer Sun <midsummer.sun@xxxxxxxxx> Date: Mon, 14 Mar 2005 13:47:47 +0530 |
Thank you so much! It looks great. I'll test it.. and see if I need anything else. One requirement is left - mapping namespaces in two XMLs. I'll probably take care of it later. If you have time, you may give a modified version of your XSLT to take care of namespaces as well. Or probably somebody else could.. I am wondering if the tool you have written is able to generate optimized xsl:template definitions(just like we write XSLT files manually) and also takes care of overlapping (nested tags).. I'll have to test it and say further.. Thanks a lot! Best regards, On Mon, 14 Mar 2005 07:57:40 +0000, Aron Bock <aronbock@xxxxxxxxxxx> wrote: > Midsummer Sun, > > Here's how I approached it. At first blush it seems extensible, but your > mileage may vary. The idea is to design a "little-language", with a grammar > and a processor. Also, unfortunately, this post is rather long, and I > couldn't make it any shorter. While one could argue that some of the > templates in the "processor" below could be combined, I prefer to start with > multiple points of abstraction. > > Anyway, here's our input file: x1.xml > > > <x> > <p a="1">1</p> > <q b="2">2</q> > </x> > > We want to turn it into this: x2.xml > > <x> > <u><w m="1">1</w></u> > <v n="2">2</v> > </x> > > First I manually wrote a stylesheet to do this. We start with an "identity > transform", then supply templates to manipulate specific nodes. Here it is: > x.xsl > > > <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > version="1.0"> > <!-- Start with identity transform: copies input to output --> > <xsl:template match="node() | @*"> > <xsl:copy> > <xsl:apply-templates select="node() | @*"/> > </xsl:copy> > </xsl:template> > > <xsl:template match="x/p"> > <u> > <w> > <xsl:apply-templates select="@*"/> > <xsl:apply-templates/> > </w> > </u> > </xsl:template> > > <xsl:template match="x/p/@a"> > <xsl:attribute name="m"> > <xsl:value-of select="."/> > </xsl:attribute> > </xsl:template> > > <xsl:template match="x/q"> > <v> > <xsl:apply-templates select="@*"/> > <xsl:apply-templates/> > </v> > </xsl:template> > > <xsl:template match="x/q/@b"> > <xsl:attribute name="n"> > <xsl:value-of select="."/> > </xsl:attribute> > </xsl:template> > </xsl:stylesheet> > > Then I created a "mapping file" with enough information so that I could > mechanixally generate the stylesheet above. I opted to go with flat <map> > elements, and to have element and attribute maps at the same level. That's > because I tend not to like special cases, but again this is just my > preference. Note that element @to mappings show just what the terminal > element will be replaced; not the new path-from-root. Here it is: map.xml > > <maps> > <map from="/x/p" to="u/w"/> > <map from="/x/p/@a" to="@m"/> > > <map from="/x/q" to="v"/> > <map from="/x/q/@b" to="@n"/> > </maps> > > Lastly I wrote a stylesheet to take map.xml as input, and to generate an > output xml file, like x.xsl, that's a stylesheet capable of transforming > x1.xml to x2.xml. It imports another stylesheet; see below. Here is > map.xsl > > > <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > version="1.0" > xmlns:xalan="http://xml.apache.org/xalan" > exclude-result-prefixes="xalan"> > > <xsl:import href="str.tokenize.xsl"/> > > <xsl:output indent="yes"/> > > <xsl:template match="/"> > <xsl:call-template name="write-stylesheet"/> > </xsl:template> > > <xsl:template name="write-stylesheet"> > <xsl:element name="xsl:stylesheet"> > <xsl:attribute > name="xmlns:xsl">http://www.w3.org/1999/XSL/Transform</xsl:attribute> > <xsl:attribute name="version">1.0</xsl:attribute> > <xsl:element name="xsl:output"> > <xsl:attribute > name="omit-xml-declaration">yes</xsl:attribute> > </xsl:element> > > <xsl:call-template name="write-identity"/> > <xsl:apply-templates select="/maps/map"/> > > </xsl:element> > </xsl:template> > > <!-- Writes identity-transform template --> > <xsl:template name="write-identity"> > <xsl:element name="xsl:template"> > <xsl:attribute name="match">node() | @*</xsl:attribute> > <xsl:element name="xsl:copy"> > <xsl:element name="xsl:apply-templates"> > <xsl:attribute name="select">node() | @*</xsl:attribute> > </xsl:element> > </xsl:element> > </xsl:element> > </xsl:template> > > <!-- Processes each mapping --> > <xsl:template match="/maps/map"> > <xsl:element name="xsl:template"> > <xsl:attribute name="match"><xsl:value-of > select="@from"/></xsl:attribute> > <xsl:call-template name="write-to"> > <xsl:with-param name="to" select="@to"/> > </xsl:call-template> > </xsl:element> > </xsl:template> > > <!-- Writes the "to" part of each mapping --> > <xsl:template name="write-to"> > <xsl:param name="to" select="/.."/> > <xsl:variable name="path"> > <xsl:call-template name="tokenize-path"> > <xsl:with-param name="path" select="$to"/> > </xsl:call-template> > </xsl:variable> > > <xsl:call-template name="write-to-path"> > <xsl:with-param name="path" > select="xalan:nodeset($path)/token"/> > </xsl:call-template> > </xsl:template> > > <!-- > Continuation of template "write-to"; writes the templates > that creates the "to" elements/attributes > --> > <xsl:template name="write-to-path"> > <xsl:param name="path" select="/.."/> > > <xsl:choose> > <xsl:when test="not($path)"> > <!-- Nothing to do (not needed; here just as safeguard) --> > </xsl:when> > <!-- If attribute, this is a 1:1 transform --> > <xsl:when test="starts-with($path[1], '@')"> > <xsl:element name="xsl:attribute"> > <xsl:attribute name="name"> > <xsl:value-of select="substring($path[1], 2)"/> > </xsl:attribute> > <xsl:element name="xsl:value-of"> > <xsl:attribute name="select">.</xsl:attribute> > </xsl:element> > </xsl:element> > </xsl:when> > <!-- Must be (we assume) an element; recursively write target > nodes, in order --> > <xsl:otherwise> > <xsl:element name="{$path[1]}"> > <xsl:choose> > <xsl:when test="not($path[position() != 1])"> > <xsl:element name="xsl:apply-templates"> > <xsl:attribute > name="select">@*</xsl:attribute> > </xsl:element> > <xsl:element name="xsl:apply-templates"/> > </xsl:when> > <xsl:otherwise> > <xsl:call-template name="write-to-path"> > <xsl:with-param name="path" > select="$path[position() != 1]"/> > </xsl:call-template> > </xsl:otherwise> > </xsl:choose> > </xsl:element> > </xsl:otherwise> > </xsl:choose> > > </xsl:template> > > <!-- Calls an utility tokenizing template --> > <xsl:template name="tokenize-path"> > <xsl:param name="path" select="/.."/> > <xsl:call-template name="tokenize"> > <xsl:with-param name="string" select="$path"/> > <xsl:with-param name="delimiters" select="'/'"/> > </xsl:call-template> > </xsl:template> > > </xsl:stylesheet> > > The stylesheet it imports is str.tokenize.xsl. I got it from Sal Mangano's > "XSLT Cookbook", and he attributes it to Jeni Tennison. Here is > str.tokenize.xsl > > > <xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" > version="1.0"> > <xsl:template name="tokenize"> > <xsl:param name="string" select="''" /> > <xsl:param name="delimiters" select="' > '" /> > > <xsl:choose> > <!-- Nothing to do if empty string --> > <xsl:when test="not($string)" /> > > <!-- No delimiters signals character level tokenization. --> > <xsl:when test="not($delimiters)"> > <xsl:call-template name="_tokenize-characters"> > <xsl:with-param name="string" select="$string" /> > </xsl:call-template> > </xsl:when> > <xsl:otherwise> > <xsl:call-template name="_tokenize-delimiters"> > <xsl:with-param name="string" select="$string" /> > <xsl:with-param name="delimiters" select="$delimiters" /> > </xsl:call-template> > </xsl:otherwise> > </xsl:choose> > </xsl:template> > > <xsl:template name="_tokenize-characters"> > <xsl:param name="string" /> > <xsl:if test="$string"> > <token><xsl:value-of select="substring($string, 1, 1)" /></token> > <xsl:call-template name="_tokenize-characters"> > <xsl:with-param name="string" select="substring($string, 2)" /> > </xsl:call-template> > </xsl:if> > </xsl:template> > > <xsl:template name="_tokenize-delimiters"> > <xsl:param name="string" /> > <xsl:param name="delimiters" /> > <xsl:param name="last-delimit"/> > <!-- Extract a delimiter --> > <xsl:variable name="delimiter" select="substring($delimiters, 1, 1)" /> > <xsl:choose> > <!-- If the delimiter is empty we have a token --> > <xsl:when test="not($delimiter)"> > <token><xsl:value-of select="$string"/></token> > </xsl:when> > <!-- If the string contains at least one delimiter we must split it --> > <xsl:when test="contains($string, $delimiter)"> > <!-- If it starts with the delimiter we don't need to handle the --> > <!-- before part --> > <xsl:if test="not(starts-with($string, $delimiter))"> > <!-- Handle the part that comes befor the current delimiter --> > <!-- with the next delimiter. If ther is no next the first test --> > <!-- in this template will detect the token --> > <xsl:call-template name="_tokenize-delimiters"> > <xsl:with-param name="string" > select="substring-before($string, $delimiter)" /> > <xsl:with-param name="delimiters" > select="substring($delimiters, 2)" /> > </xsl:call-template> > </xsl:if> > <!-- Handle the part that comes after the delimiter using the --> > <!-- current delimiter --> > <xsl:call-template name="_tokenize-delimiters"> > <xsl:with-param name="string" > select="substring-after($string, $delimiter)" /> > <xsl:with-param name="delimiters" select="$delimiters" /> > </xsl:call-template> > </xsl:when> > <xsl:otherwise> > <!-- No occurances of current delimiter so move on to next --> > <xsl:call-template name="_tokenize-delimiters"> > <xsl:with-param name="string" > select="$string" /> > <xsl:with-param name="delimiters" > select="substring($delimiters, 2)" /> > </xsl:call-template> > </xsl:otherwise> > </xsl:choose> > </xsl:template> > </xsl:stylesheet> > > Regards, > > --A > > _________________________________________________________________ > Is your PC infected? Get a FREE online computer virus scan from McAfee. > Security. http://clinic.mcafee.com/clinic/ibuy/campaign.asp?cid=3963
Current Thread |
---|
|
<- Previous | Index | Next -> |
---|---|---|
RE: [xsl] Stylesheet from a stylesh, Aron Bock | Thread | Re: [xsl] Stylesheet from a stylesh, Aron Bock |
RE: [xsl] Stylesheet from a stylesh, Aron Bock | Date | [xsl] matching an element based on , Paul Hunnisett |
Month |