Jump to content

Transforming an element in an element


quastar

Recommended Posts

Greetings,This might be a simple problem, but I couldn't find the solution to it on W3Schools' site.I want to transform an element that exist in another element. For example, I have this:

<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet href="file.xsl" type="text/xsl"?><text>  <para>	This is a text of a paragraph. Surprisingly the author put a link inside here, <url>http://w3schools.invisionzone.com</url> as part of the text.  </para>  <para>	Another text with just a bunch of words.  </para></text>

In my XSLT document, I use this code to transform my XML document:

<xsl:for-each select="/text/para">  <xsl:value-of select="current()" /></xsl:for-each>

But the output of the <xsl:value-of> will be

This is a text of a paragraph. Surprisingly the author put a link inside here, http://w3schools.invisionzone.com as part of the text.

How can I tweak the code so that it will process the <url> element?Thank you.Quastar

Link to comment
Share on other sites

Actually, the solution is present in the XSLT tutorial. However, I imagine the sample is not the best one there could be.Look at XSLT Apply. The corresponding solution for you is this one:

<xsl:template match="/text"><xsl:for-each select="para"><xsl:apply-templates/></xsl:for-each></xsl:template><xsl:template match="url"><a href="."><xsl:value-of select="."/></a></xsl:template>

Note: "." is in most cases equivalent to "current()".

Link to comment
Share on other sites

Thank you boen_robot, however the solution you gave me only managed to transform the <url> element without the output of <para> element as well. By using your code in my program, for example

<xsl:for-each select="/text/para">  <xsl:apply-templates select="url" /></xsl:for-each><xsl:template match="url"><a href="{current()}"><xsl:value-of select="current()" /></a></xsl:template>

The output will be" http://w3schools.invisionzone.com "What I want to achieve by using <xsl:value-of> earlier, and in hoping that I can transform the <url> tag that is inside of the <para> tag, is to achieve something like this:" This is a text of a paragraph. Surprisingly the author put a link inside here, http://w3schools.invisionzone.com as part of the text. "Am I missing something important here? Thanks again.

Link to comment
Share on other sites

You didn't tryed it out, did you? It works. I just tryed it:

<?xml version="1.0"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">	<xsl:output method="html"/>	<xsl:template match="/">		<xsl:for-each select="/text/para">			<p>				<xsl:apply-templates/>			</p>		</xsl:for-each>	</xsl:template>	<xsl:template match="url">		<a href=".">			<xsl:apply-templates/>		</a>	</xsl:template></xsl:stylesheet>

The important thing you're missing here are XSLT's built-in templates. They ensure that when <xsl:apply-templates/> is called, it will sooner or later match a text() node and value-of the result. Your para has 3 text nodes. One before each "url" element, one inside it and one after it. All of them are just value-of-ed. If there are any additional elements inside them, their templates will be applyed and the rest of the text nodes- value-of-ed. This all applyes only if you do not override the default template with your own of course.

Link to comment
Share on other sites

I did try as what you suggested earlier, and I know what I did wrong:

<xsl:apply-templates select="url" />

I didn't understand about the built-in concept at first, so I put the "select=" rule as well. Nevertheless, your further explanation and link to the built-in page helped me to understand what were you trying to say earlier. :)I guess I need to rewrite some parts of my code again, to make sure that I manage to do everything right.On a side note, I changed your code a little bit:

<?xml version="1.0"?><xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">	<xsl:output method="html"/>	<xsl:template match="/">		<xsl:for-each select="/text/para">			<p>				<xsl:apply-templates/>			</p>		</xsl:for-each>	</xsl:template>	<xsl:template match="url">		<a href="{.}">		<xsl:value-of select="." />		</a>	</xsl:template></xsl:stylesheet>

I put the curley brackets in so that it will transform the "." into the retreived text node, and I replaced <xsl:apply-templates /> with <xsl:value-of> in the 'url' template. Now it works perfectly fine, with the given URL will work as an URL after the transformation.Thank you, and hopefully this will help anyone else who might face the same problem. :)

Link to comment
Share on other sites

Right... AVT... forgot about that (didn't clicked on the URL in the result :) ).

Link to comment
Share on other sites

  • 2 weeks later...

Okay, everything looks fine on my code, but I found a small problem with another bit of it. I think I used this thread instead since it is related.I want to process this bit of code in my xslt file:

<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet href="file.xsl" type="text/xsl"?><data>  <people>	<address>	  Dunkirk	  Nottingham	  Nottinghamshire	  NG7 2DU	</address>  </people></data>

But I don't know how to make it show the address as stored in the xml file. If I use <xsl:value-of> for the <address> tag, the output will be:

Dunkirk Nottingham Nottinghamshire NG7 2DU

I want to make it output like this:

output:	  Dunkirk	  Nottingham	  Nottinghamshire	  NG7 2DU

Is there any code that I miss, or should I attempt to replace the white space for each line into a 'return' character?

Link to comment
Share on other sites

One thing you could do (the easiest way) is to add xml:space="preserve" to every element who's space should be preserved in the output code. Of course how this output is rendered is another story, but at least in code view, it will be right. Like so:

<address xml:space="preserve">

Another way (if adding xml:space seems a bit messy) is to use copy-of instead of value-of. In order to avoid copying the adress element itself and it's attributes alongside the text, you need to add "//text()" at the end of the match path. You could also add "//*" if the "adress" is to contain other elements. For example, if you've used

<xsl:template match="adress"><xsl:value-of select="."/></xsl:template>

you'd now use:

<xsl:template match="adress"><xsl:copy-of select=".//text()|.//*"/></xsl:template>

Link to comment
Share on other sites

I have adjusted my code as per your suggestion:

<!-- Camp Address --><xsl:if test="count(address) > 0"><h2>Address:</h2><xsl:for-each select="/data/people">  <p><xsl:apply-templates select="address" /></p></xsl:for-each></xsl:if><xsl:template match="address"><xsl:copy-of select=".//text()|.//*" /></xsl:template>

But it still produce a one-line output like before. On the other hand, I did some code-hunting on the net, and I found one code here, about 'word warp and text replace templates for XSLT', that solved my problem, albeit much more complicated then what I've seen before.This is my solution based on the help of the site:

<xsl:variable name="CR" select="'
;'" />...<!-- Camp Address --><xsl:if test="count(address) = 1"><h2>Address:</h2><p class="address"><xsl:call-template name="address">  <xsl:with-param name="textaddress" select="/data/people/address" /></xsl:call-template></p></xsl:if><xsl:template name="address"><xsl:param name="textaddress" /><xsl:variable name="textlength" select="string-length($textaddress)" /><xsl:if test="$textlength > 0">  <xsl:choose>	<xsl:when test="contains($textaddress,$CR)">	  <xsl:variable name="linebeforefirstbreak" select="substring-before($textaddress,$CR)" />	  <xsl:variable name="lineafterfirstbreak" select="substring-after($textaddress,$CR)" />	  <xsl:if test="string-length($linebeforefirstbreak) > 0">		<xsl:value-of select="$linebeforefirstbreak" /><br />	  </xsl:if>	  <xsl:if test="string-length($lineafterfirstbreak) > 0">		<xsl:call-template name="address">		  <xsl:with-param name="textaddress" select="$lineafterfirstbreak" />		</xsl:call-template>	  </xsl:if>	</xsl:when>	<xsl:otherwise>	  <xsl:value-of select="$textaddress" />	</xsl:otherwise>  </xsl:choose></xsl:if></xsl:template>

What do you think of it? Basically, it manually printed out the line by breaking it by the Carriage Return characters in the text (something similar to what I thought of earlier, but been unable to comprehend such advanced task).

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
×
×
  • Create New...