viernes, 20 de febrero de 2015

Un poco sobre HtmlAgilityPack

Tuve la necesidad de manipular un poco de HTML con C# y pues todas la búsquedas apuntaron a HtmlAgilityPack.

El problema

Por medio de un editor WYSIWYG, en modo HTML, permito la edición de contenido que se publica en un sitio web, no se trata de un CMS, pero los usuarios tienden a redactar el texto primero en Microsoft Word © y luego copiarlo al editor del sitio. El HTML que se forma incluía una serie de estilos que corrompía la maquetación del sitio.
El contenido se muestra de dos maneras, primero un breve resumen y luego el contenido completo. La dificultad se encuentra en el resumen, puesto que debe cortar el texto original podría provocar que una etiqueta quede mal construida o sin cerrar.

La solución

Lo primero que necesitaba era limpiar el HTML antes de almacenarlo en el base de datos. La limpieza consiste en eliminar todo los atributos de los elementos, el que más me molestaba era el atributo "style".

private string Clean(string html)
{
 HtmlDocument doc = new HtmlDocument();
 System.IO.StringWriter sw;
 string result;

 doc.LoadHtml(html);

 foreach(HtmlNode n in doc.DocumentNode.SelectNodes("*"))
 {
  if (n.HasAttributes)
  n.Attributes.RemoveAll();
 }
 
 sw = new System.IO.StringWriter();
 doc.Save(sw);
 doc = null;

 result = sw.ToString();

 sw.Dispose();
 sw = null;

 return result;
}

Ahora debía recortar el texto a los primeros 250 caracteres y garantizar tener un HTML bien formado. El código lo estoy utilizando desde una vista con Razor.

string tmp;
HtmlDocument doc;
System.IO.StringWriter sw;

doc = new HtmlDocument();

tmp = Model.info_a_publicar;

if (!string.IsNullOrWhiteSpace(tmp) && tmp.Length > 250)
{
 tmp = tmp.Substring(0, 250);
 tmp = tmp.Substring(0, tmp.LastIndexOf(" ")); //con esto garantizo de tener palabras completas

 sw = new StringWriter();

 doc.OptionFixNestedTags = true;
 doc.OptionAutoCloseOnEnd = true;
 doc.OptionCheckSyntax = true;
 doc.LoadHtml(tmp);
 doc.Save(sw);

 sw.Flush();
 tmp = sw.ToString();
 sw.Close();

 sw = null;
}

doc = null;
´