HTML Agility Pack strip tags NOT IN whitelist
heh, apparently I ALMOST found an answer in a blog post someone made....
using System.Collections.Generic;
using System.Linq;
using HtmlAgilityPack;
namespace Wayloop.Blog.Core.Markup
{
public static class HtmlSanitizer
{
private static readonly IDictionary<string, string[]> Whitelist;
static HtmlSanitizer()
{
Whitelist = new Dictionary<string, string[]> {
{ "a", new[] { "href" } },
{ "strong", null },
{ "em", null },
{ "blockquote", null },
};
}
public static string Sanitize(string input)
{
var htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(input);
SanitizeNode(htmlDocument.DocumentNode);
return htmlDocument.DocumentNode.WriteTo().Trim();
}
private static void SanitizeChildren(HtmlNode parentNode)
{
for (int i = parentNode.ChildNodes.Count - 1; i >= 0; i--) {
SanitizeNode(parentNode.ChildNodes[i]);
}
}
private static void SanitizeNode(HtmlNode node)
{
if (node.NodeType == HtmlNodeType.Element) {
if (!Whitelist.ContainsKey(node.Name)) {
node.ParentNode.RemoveChild(node);
return;
}
if (node.HasAttributes) {
for (int i = node.Attributes.Count - 1; i >= 0; i--) {
HtmlAttribute currentAttribute = node.Attributes[i];
string[] allowedAttributes = Whitelist[node.Name];
if (!allowedAttributes.Contains(currentAttribute.Name)) {
node.Attributes.Remove(currentAttribute);
}
}
}
}
if (node.HasChildNodes) {
SanitizeChildren(node);
}
}
}
}
I got HtmlSanitizer from here
Apparently it does not strip th tags, but removes the element altoghether.
OK, here is the solution for those who will need it later.
public static class HtmlSanitizer
{
private static readonly IDictionary<string, string[]> Whitelist;
private static List<string> DeletableNodesXpath = new List<string>();
static HtmlSanitizer()
{
Whitelist = new Dictionary<string, string[]> {
{ "a", new[] { "href" } },
{ "strong", null },
{ "em", null },
{ "blockquote", null },
{ "b", null},
{ "p", null},
{ "ul", null},
{ "ol", null},
{ "li", null},
{ "div", new[] { "align" } },
{ "strike", null},
{ "u", null},
{ "sub", null},
{ "sup", null},
{ "table", null },
{ "tr", null },
{ "td", null },
{ "th", null }
};
}
public static string Sanitize(string input)
{
if (input.Trim().Length < 1)
return string.Empty;
var htmlDocument = new HtmlDocument();
htmlDocument.LoadHtml(input);
SanitizeNode(htmlDocument.DocumentNode);
string xPath = HtmlSanitizer.CreateXPath();
return StripHtml(htmlDocument.DocumentNode.WriteTo().Trim(), xPath);
}
private static void SanitizeChildren(HtmlNode parentNode)
{
for (int i = parentNode.ChildNodes.Count - 1; i >= 0; i--)
{
SanitizeNode(parentNode.ChildNodes[i]);
}
}
private static void SanitizeNode(HtmlNode node)
{
if (node.NodeType == HtmlNodeType.Element)
{
if (!Whitelist.ContainsKey(node.Name))
{
if (!DeletableNodesXpath.Contains(node.Name))
{
//DeletableNodesXpath.Add(node.Name.Replace("?",""));
node.Name = "removeableNode";
DeletableNodesXpath.Add(node.Name);
}
if (node.HasChildNodes)
{
SanitizeChildren(node);
}
return;
}
if (node.HasAttributes)
{
for (int i = node.Attributes.Count - 1; i >= 0; i--)
{
HtmlAttribute currentAttribute = node.Attributes[i];
string[] allowedAttributes = Whitelist[node.Name];
if (allowedAttributes != null)
{
if (!allowedAttributes.Contains(currentAttribute.Name))
{
node.Attributes.Remove(currentAttribute);
}
}
else
{
node.Attributes.Remove(currentAttribute);
}
}
}
}
if (node.HasChildNodes)
{
SanitizeChildren(node);
}
}
private static string StripHtml(string html, string xPath)
{
HtmlDocument htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(html);
if (xPath.Length > 0)
{
HtmlNodeCollection invalidNodes = htmlDoc.DocumentNode.SelectNodes(@xPath);
foreach (HtmlNode node in invalidNodes)
{
node.ParentNode.RemoveChild(node, true);
}
}
return htmlDoc.DocumentNode.WriteContentTo(); ;
}
private static string CreateXPath()
{
string _xPath = string.Empty;
for (int i = 0; i < DeletableNodesXpath.Count; i++)
{
if (i != DeletableNodesXpath.Count - 1)
{
_xPath += string.Format("//{0}|", DeletableNodesXpath[i].ToString());
}
else _xPath += string.Format("//{0}", DeletableNodesXpath[i].ToString());
}
return _xPath;
}
}
I renamed the node because if I had to parse an XML namespace node it would crash on the xpath parsing.
HTML agility pack - removing unwanted tags without removing content?
I wrote an algorithm based on Oded's suggestions. Here it is. Works like a charm.
It removes all tags except strong
, em
, u
and raw text nodes.
internal static string RemoveUnwantedTags(string data)
{
if(string.IsNullOrEmpty(data)) return string.Empty;
var document = new HtmlDocument();
document.LoadHtml(data);
var acceptableTags = new String[] { "strong", "em", "u"};
var nodes = new Queue<HtmlNode>(document.DocumentNode.SelectNodes("./*|./text()"));
while(nodes.Count > 0)
{
var node = nodes.Dequeue();
var parentNode = node.ParentNode;
if(!acceptableTags.Contains(node.Name) && node.Name != "#text")
{
var childNodes = node.SelectNodes("./*|./text()");
if (childNodes != null)
{
foreach (var child in childNodes)
{
nodes.Enqueue(child);
parentNode.InsertBefore(child, node);
}
}
parentNode.RemoveChild(node);
}
}
return document.DocumentNode.InnerHtml;
}
Stripping all html tags with Html Agility Pack
Why not just return htmldoc.DocumentNode.InnerText
instead of removing all the non-text nodes? It should give you what you want.
Strip all HTML tags, except anchor tags
I suggest you use Html Agility Pack
also check this question/answers: HTML Agility Pack strip tags NOT IN whitelist
How to strip comments from HTML using Agility Pack without losing DOCTYPE
Check that comment does not start with DOCTYPE
foreach (var comment in nodes)
{
if (!comment.InnerText.StartsWith("DOCTYPE"))
comment.ParentNode.RemoveChild(comment);
}
html agility pack remove children
bodyNode.RemoveChild(functionBarNode,false);
But functionBarNode is not a child of bodyNode.
How about functionBarNode.ParentNode.RemoveChild(functionBarNode, false)
? (And forget the bit about finding bodyNode.)
Related Topics
Play and Wait for Animation/Animator to Finish Playing
Registering a Custom JSONconverter Globally in JSON.Net
Linq to SQL Using Group by and Count(Distinct)
Authorization Header Is Lost on Redirect
How to Change Symbol for Decimal Point in Double.Tostring()
How to Disable a Tab Inside a Tabcontrol
Select Parsed Int, If String Was Parseable to Int
How to Hide a Column (Gridview) But Still Access Its Value
Best Way to Access Com Objects from C#
Convert Array of Bytes to Bitmapimage
Routing: the Current Request for Action [...] Is Ambiguous Between the Following Action Methods
Loading Multiple Versions of the Same Assembly
Cannot Access a Disposed Object in ASP.NET Core When Injecting Dbcontext
Configure JSON.Net to Ignore Datacontract/Datamember Attributes
How to Seed in Entity Framework Core 2
Default Value of a Type at Runtime