9

Title says it all. Here is some example code.

String test = '\n\n\n\n'; // All New Line characters

System.debug( String.isBlank(test) ); // Returns true
System.debug( String.isEmpty(test) ); // Returns false
System.debug( test ); // Yields several empty lines in the debug output.

test = test.stripHtmlTags();

System.debug( String.isBlank(test) ); // Returns true
System.debug( String.isEmpty(test) ); // Returns true
System.debug( test ); // Yields no empty lines in the debug output

So I have a String which may or may not have HTML tags. I want to remove the HTML tags and translate <br> to new lines \n, which stripHtmlTags does fine.

My problem is that I may not always have HTML in my string, and when I do not have HTML I still want to keep my formatting.

stripHtmlTags also removed carriage return \r and tab space \t and possibly others I didn't check.


UPDATE

This is the official response from Salesforce on the issue. stripHtmlTags() is WAD and they are going to update the documentation to reflect the fact that it removes white-space characters as well as HTML tags.

As per update from the team, we have revisited the implementation of the method, we have created a Doc Bug to make note of this in our documentation. The whitespace characters in the string get stripped.

So, a Doc Bug has been logged for this to be included that whitespace kind of characters is not preserved when using this method.

5
  • 3
    That seems like a bug: whitespace are not HTML tags, and so the method is either deceptive or the docs are. This might be worth a look at by Technical Support for a more official answer.
    – sfdcfox
    Commented Feb 22, 2017 at 15:54
  • @sfdcfox I was thinking about submitting a case with Salesforce, but wanted to check with community to make sure it wasn't just me.
    – dBeltowski
    Commented Feb 22, 2017 at 15:56
  • @sfdcfox Doc Bug :(
    – dBeltowski
    Commented Mar 15, 2017 at 14:51
  • Its a Design Bug, being called a Doc Bug, and nearly 18 months later that Doc Bug is still open. Commented Aug 15, 2018 at 13:04
  • And there are other bugs. New answer added. Commented Aug 15, 2018 at 14:51

4 Answers 4

7

Seems like its desired functionality.

stripHtmlTags removes all the formatting by HTML, \n also generates some white space. so maybe due to that it removes it.

One workaround would be to replace backslash or other desired character with a specific ones, then after calling stripHtmlTags(), restore the characters.

String backlash = '\n';
String backlashReplacement = '---n';

String test = '--&nbsp;\n\n<b>test</b>\n\n--';

test = test.replaceAll(backlash, backlashReplacement);

System.debug( test );

test = test.stripHtmlTags();

test = test.replaceAll(backlashReplacement, backlash);

System.debug( test );
3

As of Summer 18, the docs are not updated. However, "The whitespace characters in the string get stripped," is not the whole story, despite what Salesforce said. The following assertions will all pass.

The first assertions shows that .stripHtmlTags() does strip newlines, and does not add newlines when stripping <p>tags:

System.assertEquals('Para1 Para2 Para3', 
                    '<p>Para1</p>\n<p>Para2</p><p>Para3</p>'.stripHtmlTags());

The second assertion shows that .stripHtmlTags() will add newlines when removing <br/> tags. Also note the added space between the newline and "Para2" in the stripped text.

System.assertEquals('Para1\n Para2 Para3', 
                    '<p>Para1</p><br/><p>Para2</p><p>Para3</p>'.stripHtmlTags());

The third assertion shows that newlines inside a <pre> block are not removed. Again, notice the extra space between the final newline and "Para1"

System.assertEquals('one\ntwo\n\nthree\n\n\n Para1 Para2',
                    '<pre>one\ntwo\n\nthree\n\n\n</pre><p>Para1</p><p>Para2</p>'.stripHtmlTags());

You could argue whether stripping newlines is considered a bug, but I think stripping newlines while adding newlines for <br/> tags is bug, and I think not adding newlines after paragraphs while maintaining newlines inside <pre> blocks is also a bug. Regardless, for my use case, I needed to convert an arbitrary block of html (such as from an html-formatted email body) into plain text for display, with reasonable newline handling; my current version is below. It uses a newline replacement value like @Raul's answer, though I chose ~~~ as my temporary newline text. You could use any uncommon sequence as long as you change it in both places it is used.

// strip html tags while handling new lines reasonably:
//  * replace closing </p>, </li>, </div>, </h1> (h2, h3, etc), </tr>, </dt>, 
//    and </blockquote> elements with a newline
//  * replace self-closing <p/>, <br/>, and <hr> tags with newlines
//  * maintain newlines inside <pre> blocks
public static String getPlainTextForHtml(String html) {
  return html.replaceAll('<(?:/(?:p|li|div|h\\d|tr|dt|blockquote)|(?:p)/)>', '$0~~~')
               .stripHtmlTags()
               .replaceAll('~~~', '\n');
}
2
  • 1
    New to this and can't edit. Trying to add that ignore case should be considered in the fix, so include an i after the first ? like this: '<(?i:/
    – Crocket
    Commented Apr 8, 2019 at 19:38
  • @Crocket Welcome to the site. You can't edit yet, so I've moved your answer to be a comment on this one, and updated it slightly to refrlect what I think you were trying to do. Do stick around :)
    – Matt Lacey
    Commented Apr 9, 2019 at 1:17
2

I believe is expected functionality. Since you are using \r \n \t in code it's converting it to the proper html on output. So when you strip them using String.stripHtmlTags()it's going to strip all html tags.

If you're displaying this text you could use <apex:outputPanel escapeHTML="true"> then the formatting will stay the same, and you won't have to use stripHtmlTags() and the formatting will stay the same

2
  • This is not for use in a Visualforce page unfortunately. When you say "Since you are using \r \n \t in code it's converting it to the proper html on output" I'm not sure I understand. My string may look like this 'test\ntest' or it may look like this 'test<br>test'. I would think that in the first case \n would not be considered HTML and thus not get stripped?
    – dBeltowski
    Commented Feb 22, 2017 at 14:48
  • Ahh ok. I wish Salesforce's documentation was a little more clear on this. But I still think it's desired functionality. It seems like Rahul's answer might be a good workaround. Commented Feb 22, 2017 at 14:59
2

It seems to me that you have a couple of options here. The easiest would seem to be to use RegEx along with a pattern matcher class where the capture groups to use would look something like (/i)((<br>)(/n)(/t)(/r)).

You could also look for the indexes of those characters and/or groups, then find strings in between them.

Assuming string S contains the html to be stripped, perhaps a more direct method, but somewhat messy solution, would be along the lines of the following:

string S; // contains the string to operate on

S.replace(<BR>, /r); // for upper case
S.replace(<br>, /r); // for lower case

list<integer>indexR = new list<integer>();
indexR.add(S.indexOfIgnoreCase(/r));

list<integer>indexN = new list<integer>();
indexN.add(S.indexOfIgnoreCase(/n));

list<integer>indexT = new list<integer>();
indexT.add(S.indexOfIgnoreCase(/t));

for(integer i=indexR[0]+1,i<S.length(),i++){
    indexR.add(S.indexOfIgnoreCase(S, /r));

    if(indexR[i] = -1){ //no match found
       indexR[i] = S.length() +1; // set value beyond last index 
       i = S.length(); // causes loop to end 
    }    
}

for(integer i=indexN[0]+1,i<S.length(),i++){
    indexN.add(S.indexOfIgnoreCase(S, /n));

    if(indexN[i] = -1){ //no match found
       indexN[i] = S.length() +1; // set value beyond last index
       i = S.length(); // causes loop to end
    }   
}

for(integer i=indexN[0]+1,i<S.length(),i++){
    indexT.add(S.indexOfIgnoreCase(S, /t));

    if(indexT[i] = -1){ //no match found
       indexT[i] = S.length() +1; // set value beyond last index 
       i = S.length(); // causes loop to end
    }   
}

string RS = S.split(((/n)(/t)(/r))); 
// this is a regular expression inside the outer parens
// splits into list on any of the above

map<integer,string>indexToChar = new map<integer,string>();

for(i=0,i<S.length(),i++)
    if(indexN[i] = i){
        indexToChar.put(i,'/n');
    }
    if(indexR[i] = i){
        indexToChar.put(i,'/r');
    }
    if(indexT[i] = i){
        indexToChar.put(i,'/t');
    }
}

String final = '';
integer count = 0;

for(string a:RS){
    a.stripHtmlTags;
    if(indexToChar.keyset().contains(count){
        a += indexToChar.get(count);
    } 

    final += a;
    count ++;
}

final is the string stripped of html with other characters preserved.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .