Wrox Programmer Forums
Go Back   Wrox Programmer Forums > XML > XSLT
|
XSLT General questions and answers about XSLT. For issues strictly specific to the book XSLT 1.1 Programmers Reference, please post to that forum instead.
Welcome to the p2p.wrox.com Forums.

You are currently viewing the XSLT section of the Wrox Programmer to Programmer discussions. This is a community of software programmers and website developers including Wrox book authors and readers. New member registration was closed in 2019. New posts were shut off and the site was archived into this static format as of October 1, 2020. If you require technical support for a Wrox book please contact http://hub.wiley.com
 
Old December 15th, 2004, 05:09 PM
Registered User
 
Join Date: Dec 2004
Posts: 8
Thanks: 0
Thanked 0 Times in 0 Posts
Default help changing text, and deleting elements...

I'm trying to find out if there is a way to use XSLT to transform or filter "dirty words" out of XML documents. Also, if there is a way to erase an entire element and its contents from showing up in the output. For example, if this is a document:
<transmission>
    <name>Joseph Schmo
        <classification>secret</classification>
    </name>
    <message>Hi the target is anywhere</message>
    <example>this element and these contents need to be erased</example>
    <dirtywords>hi target anywhere</dirtywords>
</transmission>

And I want to change:
secret -> _ (blankspace)
hi -> hello
target -> objective
anywhere -> somewhere

and eliminate the <example> element and its contents.

I have found a way to eliminate one word using this stylsheet that i found in the posts:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:param name="what" select="'hi'"/>
 <xsl:param name="by-what" select="'hello'"/>
    <xsl:template match="/">
        <xsl:apply-templates/>
    </xsl:template>

    <xsl:template match="node() | @*">
        <xsl:choose>
            <xsl:when test="self::text()">
                <xsl:variable name="this-text" select="self::text()"/>
                <xsl:choose>
                    <xsl:when test="contains($this-text, $what)">
                        <xsl:call-template name="replace">
                            <xsl:with-param name="str" select="$this-text"/>
                            <xsl:with-param name="what" select="$what"/>
                            <xsl:with-param name="by-what" select="$by-what"/>
                        </xsl:call-template>
                    </xsl:when>
                    <xsl:otherwise>
                        <xsl:copy-of select="."/>
                    </xsl:otherwise>
                </xsl:choose>
            </xsl:when>
            <xsl:otherwise>
                <xsl:copy>
                    <xsl:apply-templates select="node() | @*"/>
                </xsl:copy>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
    <xsl:template name="replace">
        <xsl:param name="str"/>
        <xsl:param name="what"/>
        <xsl:param name="by-what"/>
        <xsl:choose>
            <xsl:when test="not($str)"/>
            <xsl:when test="starts-with($str, $what)">
                <xsl:value-of select="$by-what"/>
                <xsl:call-template name="replace">
                    <xsl:with-param name="str" select="substring($str, string-length($what)+1)"/>
                    <xsl:with-param name="what" select="$what"/>
                    <xsl:with-param name="by-what" select="$by-what"/>
                </xsl:call-template>
            </xsl:when>
            <xsl:otherwise>
                <xsl:value-of select="substring($str, 1, 1)"/>
                <xsl:call-template name="replace">
                    <xsl:with-param name="str" select="substring($str, 2)"/>
                    <xsl:with-param name="what" select="$what"/>
                    <xsl:with-param name="by-what" select="$by-what"/>
                </xsl:call-template>
            </xsl:otherwise>
        </xsl:choose>
    </xsl:template>
</xsl:stylesheet>

However, I don't know how to check for multiple words or eliminate elements and content. This is the output I get:
<transmission>
<name>Joseph Schmo
<classification>secret</classification>
</name>
<message>Hi the target is anywhere</message>
<example>thellos element and these contents need to be erased</example>
<dirtywords>hello target anywhere</dirtywords>
</transmission>

So, it kind of works...
Any help would be greatly appreciated by this newbie!!


~Ryan
 
Old December 15th, 2004, 08:16 PM
Authorized User
 
Join Date: Nov 2004
Posts: 81
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via ICQ to jkmyoung
Default

with what you have now, filtering out an element is pretty easy.
<xsl:param name="erase" select="'example'"/>
At the top of your <xsl:choose> have the first choice be
<xsl:when test="name() = $erase" />

In other words, when we match the name with your param, we don't output anything at all.

Should be a way to filter out multiple arguments, might be hard though. Have to think about it. would probably be recursive.
--
instead of what you have right now, I recommend creating a recursive template which filters out words, if you pass in a string.

You'd need to pass in a global variable with the things you want to replace. I recommend a structure such as
<filters>
<filter text="target" replace="objective"/>
<filter text="anywhere" replace="somewhere"/>
<filter text="hi" replace="hello"/>
<filter text="secret" replace=" "/>
</filters>

Say this is in a variable called filters
<xsl:template name="filter">
<xsl:param name="str"/>
<xsl:choose>
<xsl:when test="$filters/filter[contains($str, @text)]">
    <xsl:variable name="match" select="$filters/filter[contains($str, @text)]"/>
    <xsl:variable name="word" select="$match/@text"/>
    <xsl:variable name="repl" select="$match/@replace"/>
    <xsl:call-template name="filter">
     <xsl:with-param name="str">
        <xsl:value-of select="substring-before($str,$word)"/>
        <xsl:value-of select="$repl"/>
        <xsl:value-of select="substring-after($str,$word)"/>
     </xsl:with-param>
    </xsl:call-template>
</xsl:when>
<xsl:otherwise>
    <xsl:copy-of select="$str"/>
</xsl:otherwise>
</xsl:choose>
</xsl:template>

Call the template with:
<xsl:call-template name="filter">
<xsl:with-param name="str" select="self::text()"/>
</xsl:call-template>

Might not be the fastest way though. You can improve it.
 
Old December 17th, 2004, 11:52 AM
Registered User
 
Join Date: Dec 2004
Posts: 8
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Without worrying about eliminating elements (i've learned how to do that pretty easily now), where have I gone wrong? I think I totally botched up putting this together...

<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:variable name="filters">
<filters>
  <filter text="target" replace="objective"/>
  <filter text="anywhere" replace="somewhere"/>
  <filter text="hi" replace="hello"/>
  <filter text="secret" replace=" "/>
</filters>
</xsl:variable>
<xsl:template name="filter">
<xsl:param name="str"/>
<xsl:choose>
  <xsl:when test="$filters/filter[contains($str, @text)]">
    <xsl:variable name="match" select="$filters/filter[contains($str, @text)]"/>
    <xsl:variable name="word" select="$match/@text"/>
    <xsl:variable name="repl" select="$match/@replace"/>
    <xsl:call-template name="filter">
      <xsl:with-param name="str">
        <xsl:value-of select="substring-before($str,$word)"/>
        <xsl:value-of select="$repl"/>
        <xsl:value-of select="substring-after($str,$word)"/>
      </xsl:with-param>
    </xsl:call-template>
  </xsl:when>
  <xsl:otherwise>
    <xsl:copy-of select="$str"/>
  </xsl:otherwise>
</xsl:choose>
</xsl:template>

<xsl:template match="/">
<xsl:call-template name="filter">
  <xsl:with-param name="str" select="self::text()"/>
</xsl:call-template>
</xsl:template>

</xsl:stylesheet>

I get an error "cannot use result tree fragmentError in XPath expression, Cannot use result tree fragment" for the line:
<xsl:when test="$filters/filter[contains($str, @text)]">

I've been reading [u]XSLT Quickly</u> and have learned a lot, but not quite enough for this I think...


~Ryan
 
Old December 17th, 2004, 01:46 PM
Authorized User
 
Join Date: Nov 2004
Posts: 81
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via ICQ to jkmyoung
Default

Oh, I think I misled you: remove the root filters tag in the variable for the quickest fix. (otherwise, you'd have to add the "filters/" root whenever you wanted to reference the variable.

<xsl:variable name="filters">
<filter text="target" replace="objective"/>
<filter text="anywhere" replace="somewhere"/>
<filter text="hi" replace="hello"/>
<filter text="secret" replace=" "/>
</xsl:variable>

using msxml? Result trees are one of the big peculiarities. needs a node-set.
<xsl:when test="mxsml:node-set($filters)/filter[contains($str, @text)]">

And in the stylesheet element add
xmlns:msxml="urn:schemas-microsoft-com:xslt" extension-element-prefixes="msxml" so it knows where you're getting this from.
 
Old December 21st, 2004, 11:58 AM
Registered User
 
Join Date: Dec 2004
Posts: 8
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Ah, that was an oversight on my part...thanks for correcting me!

However,...I'm still getting the same error for that line...I'm not sure why. Everything makes sense to me for the most part, the only thing that I can think of is that it's having trouble with using variables for search and replace??? Every other stylesheet that I've used has required the use of parameters. Here is some simpler search and replace code from XSLT Quickly that works well, but again...only for one word at a time. I've tried doing it the long way (cutting and pasting the same template with parameters renamed, and a different search string) thinking that it would work, but it only uses the last template.

<xsl:template name="Replace1">
    <xsl:param name="output1"/>
    <xsl:param name="target1"/>
    <xsl:param name="replacement1"/>
    <xsl:choose>
        <xsl:when test="contains($output1,$target1)">
            <xsl:value-of select="concat(substring-before($output1,$target1),$replacement1)"/>
            <xsl:call-template name="Replace1">
                <xsl:with-param name="output1" select="substring-after($output1,$target1)"/>
                <xsl:with-param name="target1" select="$target1"/>
                <xsl:with-param name="replacement1" select="$replacement1"/>
            </xsl:call-template>
        </xsl:when>
        <xsl:otherwise>
            <xsl:value-of select="$output1"/>
        </xsl:otherwise>
    </xsl:choose>
</xsl:template>

<xsl:template match="text()">
    <xsl:call-template name="Replace1">
    <xsl:with-param name="output1" select="."/>
    <xsl:with-param name="target1" select="'attribute '"/>
    <xsl:with-param name="replacement1" select="'Attribute '"/>
    </xsl:call-template>
</xsl:template>

<xsl:template match="@*|*">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>

I think i'm about ready to give up!! I just can't figure out why the code that you showed me keeps returning that error. (btw,..i'm using XML Spy as my editor). Thanks for all your help!!!

~Ryan
 
Old December 21st, 2004, 01:56 PM
Authorized User
 
Join Date: Nov 2004
Posts: 81
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via ICQ to jkmyoung
Default

Is it XMLSpy 2004? The xslt engine in xmlspy 2004 only supports xslt 1.0, which doesn't have allow node-sets from variables.
Tools - Options - XSL : switch to Microsoft XML Parser (MSXML)
and then use the msxml:node-set extension.

If you're using 2005 then can't tell offhand, you'd have to post the error.

---
If you wanted to use the replace1 code, you'd probably have to do something like:
Code:
<xsl:template match="text()">
  <xsl:call-template name="Replace1">
    <xsl:with-param name="output1">
      <xsl:call-template name="Replace1">
      <xsl:with-param name="output1" select="."/>
      <xsl:with-param name="target1" select="'attribute '"/>
      <xsl:with-param name="replacement1" select="'Attribute '"/>
      </xsl:call-template>
    </xsl:with-param>
    <xsl:with-param name="target1" select="'hi '"/>
    <xsl:with-param name="replacement1" select="'hello '"/>
  </xsl:call-template>
</xsl:template>
using the output from 1 as the parameter for the next.

 
Old December 21st, 2004, 02:46 PM
Registered User
 
Join Date: Dec 2004
Posts: 8
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Yes!! The replace1 works perfectly. I take it that I just keep nesting the elements for the more words that I want to add like such:

<xsl:template match="text()">
      <xsl:call-template name="Replace1">
          <xsl:with-param name="output1">
              <xsl:call-template name="Replace1">
                  <xsl:with-param name="output1">
                      <xsl:call-template name="Replace1">
                          <xsl:with-param name="output1" select="."/>
                          <xsl:with-param name="target1" select="'hi '"/>
                          <xsl:with-param name="replacement1" select="'hello '"/>
                       </xsl:call-template>
                   </xsl:with-param>
                   <xsl:with-param name="target1" select="'target '"/>
                   <xsl:with-param name="replacement1" select="'objective '"/>
            </xsl:call-template>
        </xsl:with-param>
        <xsl:with-param name="target1" select="'anywhere '"/>
        <xsl:with-param name="replacement1" select="'somewhere '"/>
    </xsl:call-template>
</xsl:template>


If there were potentially hundreds of words to check, that would get a little bit trying to create though...

I'm using 2005. The error code that comes up when I try to run the 'filter' template is:
"cannot use result tree fragmentError in XPath expression, Cannot use result tree fragment"
2005 does have support for v2.0. I'll keep playing with that and see if it's possible for it to work, otherwise I'll use MSXML :)

Thanks!!
~Ryan
 
Old December 21st, 2004, 03:39 PM
Authorized User
 
Join Date: Nov 2004
Posts: 81
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via ICQ to jkmyoung
Default

Ah wait!
<xsl:stylesheet version="1.0"
you need to change version to 2.0!



 
Old December 21st, 2004, 04:02 PM
Registered User
 
Join Date: Dec 2004
Posts: 8
Thanks: 0
Thanked 0 Times in 0 Posts
Default

Changed it to 2.0 I no longer get the error message!

However, I don't get any output either. Just a blank page with
<?xml version="1.0" encoding="UTF-8"?>

I've tried starting the sheet like this:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">

and like this:
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:fn="http://www.w3.org/2004/07/xpath-functions" xmlns:xdt="http://www.w3.org/2004/07/xpath-datatypes">
    <xsl:output method="xml" version="1.0" encoding="UTF-8" indent="yes"/>



I wonder if maybe the error is somewhere in this part?:

<xsl:template match="/">
<xsl:call-template name="filter">
  <xsl:with-param name="str" select="self::text()"/>
</xsl:call-template>
</xsl:template>

<xsl:template match="@*|*">
    <xsl:copy>
        <xsl:apply-templates select="@*|node()"/>
    </xsl:copy>
</xsl:template>


At least there's no more error message!

~Ryan
 
Old December 21st, 2004, 08:11 PM
Authorized User
 
Join Date: Nov 2004
Posts: 81
Thanks: 0
Thanked 0 Times in 0 Posts
Send a message via ICQ to jkmyoung
Default

<xsl:template match="/">
<xsl:call-template name="filter">
  <xsl:with-param name="str" select="self::text()"/>
</xsl:call-template>
</xsl:template>
Where's the apply-templates in this template?, otherwise we'll end at the document node and that's that...
Can I see how you've put the entire xslt sheet together?






Similar Threads
Thread Thread Starter Forum Replies Last Post
Creating, Writing to and Deleting Text Files goels Access VBA 7 January 30th, 2007 11:51 AM
Tags as text in XML elements janise XML 16 January 2nd, 2007 06:45 AM
Changing between bold and plain text in a text box funkybuddha Access 2 January 3rd, 2006 10:15 AM
Deleting text from a RichTextBox wslyhbb C# 0 October 29th, 2005 08:00 PM
Deleting text strings automatically from Database palvin SQL Server 2000 15 October 3rd, 2005 12:52 PM





Powered by vBulletin®
Copyright ©2000 - 2020, Jelsoft Enterprises Ltd.
Copyright (c) 2020 John Wiley & Sons, Inc.