Net4x.HtmlCleaner 1.4.0.26249

dotnet add package Net4x.HtmlCleaner --version 1.4.0.26249
                    
NuGet\Install-Package Net4x.HtmlCleaner -Version 1.4.0.26249
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Net4x.HtmlCleaner" Version="1.4.0.26249" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Net4x.HtmlCleaner" Version="1.4.0.26249" />
                    
Directory.Packages.props
<PackageReference Include="Net4x.HtmlCleaner" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Net4x.HtmlCleaner --version 1.4.0.26249
                    
#r "nuget: Net4x.HtmlCleaner, 1.4.0.26249"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Net4x.HtmlCleaner@1.4.0.26249
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Net4x.HtmlCleaner&version=1.4.0.26249
                    
Install as a Cake Addin
#tool nuget:?package=Net4x.HtmlCleaner&version=1.4.0.26249
                    
Install as a Cake Tool

Net4x.HtmlCleaner

Turns messy, real-world HTML into well-formed XHTML.

It is built for the HTML that arrives from Microsoft Word and Outlook, from RSS feeds, and from WYSIWYG editors: unclosed tags, <o:p> and <w:…> namespace noise, inline styles, event handlers and CDATA leftovers. A dedicated pass strips a "Save as Web Page" export back to its content. The output is always parseable as XML, so it can be fed straight into XmlDocument, XDocument, an XSLT transform or a template engine.

Targets .NET Framework 4.5, .NET Framework 4.6.1 and .NET Standard 2.0.

Install

dotnet add package Net4x.HtmlCleaner

Usage

Everything goes through CleanerHelper.Instance, which exposes three overloads of GenerateXHtml. Each takes two independent switches: filterOutput for the tag/attribute whitelist, and cleanWordMarkup for the Word clean up.

From a string

using HtmlCleaner;

string xhtml = CleanerHelper.Instance.GenerateXHtml("<p>unclosed paragraph");
// <html><p>unclosed paragraph</p></html>

From a file

CleanerHelper.Instance.GenerateXHtml(@"C:\in\word-export.html", @"C:\out\clean.xhtml");

From any reader to any writer

using (var input = new StreamReader(sourceStream))
using (var output = new StreamWriter(destinationStream))
{
    CleanerHelper.Instance.GenerateXHtml(input, output);
}

The reader and the writer stay yours: the library flushes the writer but never closes either of them, so you can keep writing to the same destination afterwards.

What it always does

Input Output
<p>unclosed paragraph <html><p>unclosed paragraph</p></html>
<o:p>word noise</o:p><p>keep me</p> <html><p>keep me</p></html>
<p>Tom & Jerry</p> <html><p>Tom &amp; Jerry</p></html>
<p>He said "hello"</p> <html><p>He said &quot;hello&quot;</p></html>
<p>a + U+00A0 + b</p> (a non-breaking space) <html><p>a&nbsp;b</p></html>
  • Every element the source left open is closed, so the result is well-formed XML.
  • Elements carrying a namespace prefix — <o:p>, <w:WordDocument>, <v:shape> — are dropped together with their content, however many of them follow one another.
  • &, <, >, ' and " are escaped, and non-breaking spaces become &nbsp;.
  • Tag casing is preserved: <P> stays <P>.

Because &nbsp; is not one of the five entities XML predefines, declare it before parsing the result standalone:

const string prologue = "<!DOCTYPE html [<!ENTITY nbsp \"&#160;\">]>";
var document = new XmlDocument { XmlResolver = null };
document.LoadXml(prologue + xhtml);

Filtering

Pass filterOutput: true for a whitelist pass on top — useful when the HTML is going to be embedded in a page you control.

string safe = CleanerHelper.Instance.GenerateXHtml(
    "<p class='intro' onclick='steal()' style='color:red'>text</p>",
    filterOutput: true);
// <dd><p class="intro">text</p></dd>

With filtering on:

  • Tags outside the allowed list are renamed to the replacement tag (dd by default) — the content survives, the element does not. Allowed by default: p b i u em big small div img span blockquote code pre br hr ul ol li del ins strong a font dd dt. Note that html and body are not on that list, which is why the sample above is wrapped in <dd>.
  • Attributes outside the allowed list are dropped, which is what removes onclick, style and the mso-* noise. Allowed by default: class href target border src align width height color size. The names that survive are written in lower case.
  • Comments are dropped.
  • Text is trimmed, newlines (\r\n, \r and \n alike) become spaces, and runs of consecutive spaces or &nbsp; collapse to one.

Tuning the whitelists

The lists live on HtmlWriter, so drive it directly when the defaults do not fit:

using HtmlCleaner.Text;

var builder = new StringBuilder();
using (var reader = new HtmlReader(dirtyHtml))
using (var writer = new HtmlWriter(builder))
{
    writer.FilterOutput = true;
    writer.AllowedTags = new[] { "p", "b", "i", "a" };
    writer.AllowedAttributes = new[] { "href" };
    writer.ReplacementTag = "span";
    writer.ReduceConsecutiveSpace = false;

    reader.Read();
    while (!reader.EOF) writer.WriteNode(reader, true);
}
// writer must be closed - as it is here by the using - for the document to be completed
string xhtml = builder.ToString();
Member Default Effect
FilterOutput false Master switch for everything in this section.
AllowedTags see above Tags allowed through unchanged.
ReplacementTag "dd" What a disallowed tag is renamed to.
AllowedAttributes see above Attributes allowed through.
RemoveNewlines true Newlines in text become spaces.
ReduceConsecutiveSpace true Runs of spaces or &nbsp; collapse to one.

Cleaning a Word export

cleanWordMarkup: true strips what Microsoft Word adds when a document is saved as HTML. On a real five-page contract exported from Word 15, that is 514 KB down to 14 KB with every paragraph, table and character intact.

CleanerHelper.Instance.GenerateXHtml(
    @"C:\in\DraftDonatie_Final.htm",
    @"C:\out\clean.html",
    cleanWordMarkup: true);

What it removes:

Junk What happens to it
<meta>, <link> pointing at the _files folder, <style>, <script> removed with their content
`` settings blocks removed — all comments are
<o:p>, <w:…>, <v:…> elements and the xmlns:o/xmlns:w/xmlns:v declarations removed
class=MsoNormal, style='mso-…', lang=EN-GB — every attribute removed
<span>, <div>, <font> wrappers that only carried those attributes removed, children kept
the blank line each removed element left behind removed

What survives: <html><head><body>, the text, and the semantic markup — p, b, i, br, table, tr, td, headings, lists.

<html><head></head><body>
<p><b>CONTRACT DE DONAŢIE</b></p>
<p>Între subsemnaţii:</p>
…
<table><tr><td><p>DONATORI,<br />IONIŢĂ GRIGORE</p></td></tr></table>
</body></html>

Void elements are written self-closing (<br />), everything else gets a full end tag (<p></p>), so a browser reads the result the same way an XML parser does.

The two switches compose: add filterOutput: true to also squeeze the whitespace and apply the whitelists to what is left.

Character encoding

A Word export is written in the ANSI code page of the machine that saved it — usually windows-1252 — and says so in a meta element. The file overloads honour that declaration (and a byte order mark before it), falling back to UTF-8, so accented text survives:

// <meta http-equiv=Content-Type content="text/html; charset=windows-1252">
CleanerHelper.Instance.GenerateXHtml(inputFile, outputFile, cleanWordMarkup: true);
// → "cetăţean român", not "cet??ean rom?n"

This applies to the plain path too. The string and reader overloads take text you have already decoded, so the encoding is yours to pick there.

Tuning the Word clean up

The rules live on WordHtmlWriter as plain lists, so they can be retargeted at another producer:

Member Default
DiscardedElements meta link style script base basefont xml — dropped with their content
UnwrappedElements span font div — dropped, children kept
VoidElements area base basefont br col embed frame hr img input isindex link meta param source track wbr
KeepAttributes false
KeepComments false
var builder = new StringBuilder();
using (var reader = new HtmlReader(wordHtml))
using (var writer = new WordHtmlWriter(builder))
{
    writer.KeepAttributes = true;                        // keep href and src
    writer.UnwrappedElements = new[] { "span", "font" }; // but keep the divs
    writer.WriteCleanedDocument(reader);
}

Parsing without cleaning

SgmlReaderHelper is the thin wrapper over the SGML reader, with none of the HtmlCleaner filtering — it only balances the document. Use it when you want the HTML repaired but nothing removed:

string xml = new SgmlReaderHelper().ProcessString("<o:p>noise</o:p><p>keep</p>");
// <html><o:p xmlns:o="#unknown">noise</o:p><p>keep</p></html>

Unknown prefixes are bound to a placeholder namespace so that the result is still well-formed XML.

Types

Type Purpose
HtmlCleaner.CleanerHelper Static entry point; Instance returns the shared ICleanerHelper.
HtmlCleaner.Interfaces.ICleanerHelper The three GenerateXHtml overloads, for mocking.
HtmlCleaner.Text.HtmlReader SgmlReader which skips prefixed elements.
HtmlCleaner.Text.HtmlWriter XmlTextWriter which escapes text and applies the whitelists.
HtmlCleaner.Text.WordHtmlWriter HtmlWriter which removes the Word export scaffolding.
HtmlCleaner.SgmlReaderHelper Balance-only helper, no filtering.

Notes

  • GenerateXHtml throws ArgumentNullException for a null reader or writer, and the usual System.IO exceptions (FileNotFoundException, DirectoryNotFoundException, …) for the file overload.
  • The instance behind CleanerHelper.Instance is a stateless singleton and is safe to share; a single HtmlReader/HtmlWriter pair, like any XmlReader/XmlWriter, is not thread-safe.
  • Processing is logged at info level through CoreLibrary.Logging.

License

Copyright (c) Piero Viano. All rights reserved.

Product Compatible and additional computed target framework versions.
.NET net5.0 was computed.  net5.0-windows was computed.  net6.0 was computed.  net6.0-android was computed.  net6.0-ios was computed.  net6.0-maccatalyst was computed.  net6.0-macos was computed.  net6.0-tvos was computed.  net6.0-windows was computed.  net7.0 was computed.  net7.0-android was computed.  net7.0-ios was computed.  net7.0-maccatalyst was computed.  net7.0-macos was computed.  net7.0-tvos was computed.  net7.0-windows was computed.  net8.0 was computed.  net8.0-android was computed.  net8.0-browser was computed.  net8.0-ios was computed.  net8.0-maccatalyst was computed.  net8.0-macos was computed.  net8.0-tvos was computed.  net8.0-windows was computed.  net9.0 was computed.  net9.0-android was computed.  net9.0-browser was computed.  net9.0-ios was computed.  net9.0-maccatalyst was computed.  net9.0-macos was computed.  net9.0-tvos was computed.  net9.0-windows was computed.  net10.0 was computed.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
.NET Core netcoreapp2.0 was computed.  netcoreapp2.1 was computed.  netcoreapp2.2 was computed.  netcoreapp3.0 was computed.  netcoreapp3.1 was computed. 
.NET Standard netstandard2.0 is compatible.  netstandard2.1 was computed. 
.NET Framework net45 is compatible.  net451 was computed.  net452 was computed.  net46 was computed.  net461 is compatible.  net462 was computed.  net463 was computed.  net47 was computed.  net471 was computed.  net472 was computed.  net48 was computed.  net481 was computed. 
MonoAndroid monoandroid was computed. 
MonoMac monomac was computed. 
MonoTouch monotouch was computed. 
Tizen tizen40 was computed.  tizen60 was computed. 
Xamarin.iOS xamarinios was computed. 
Xamarin.Mac xamarinmac was computed. 
Xamarin.TVOS xamarintvos was computed. 
Xamarin.WatchOS xamarinwatchos was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages (5)

Showing the top 5 NuGet packages that depend on Net4x.HtmlCleaner:

Package Downloads
Net4x.CefSharp.Library

Package Description

Net4x.CefSharp.2012.Library.x64

Package Description

Net4x.CefSharp.2012.Library.x86

Package Description

Net4x.Browser2012Library

Package Description

Net4x.BrowserLibrary

Package Description

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.4.0.26249 51 9/6/2026
1.4.0 683 3/31/2025
1.1.0 505 8/27/2023
1.1.0-at20230506042041 278 5/7/2023