Error de paquete de agilidad HTMl analizando y devolviendo XElement

.net-3.5 c# html-agility-pack html-parsing

Pregunta

Puedo analizar el documento y generar una salida; sin embargo, la salida no se puede analizar en un XElement debido a una etiqueta ap, todo lo demás dentro de la cadena se analiza correctamente.

Mi entrada:

var input = "<p> Not sure why is is null for some wierd reason!<br><br>I have implemented the auto save feature, but does it really work after 100s?<br></p> <p> <i>Autosave?? </i> </p> <p>we are talking...</p><p></p><hr><p><br class=\"GENTICS_ephemera\"></p>";

Mi código:

public static XElement CleanupHtml(string input)
    {  


    HtmlAgilityPack.HtmlDocument htmlDoc = new HtmlAgilityPack.HtmlDocument();

    htmlDoc.OptionOutputAsXml = true;
    //htmlDoc.OptionWriteEmptyNodes = true;             
    //htmlDoc.OptionAutoCloseOnEnd = true;
    htmlDoc.OptionFixNestedTags = true;

    htmlDoc.LoadHtml(input);

    // ParseErrors is an ArrayList containing any errors from the Load statement
    if (htmlDoc.ParseErrors != null && htmlDoc.ParseErrors.Count() > 0)
    {

    }
    else
    {

        if (htmlDoc.DocumentNode != null)
        {
            var ndoc = new HtmlDocument(); // HTML doc instance
            HtmlNode p = ndoc.CreateElement("body");  

            p.InnerHtml = htmlDoc.DocumentNode.InnerHtml;
            var result = p.OuterHtml.Replace("<br>", "<br/>");
            result = result.Replace("<br class=\"special_class\">", "<br/>");
            result = result.Replace("<hr>", "<hr/>");
            return XElement.Parse(result, LoadOptions.PreserveWhitespace);
        }
    }
    return new XElement("body");

}

Mi salida:

<body>
   <p> Not sure why is is null for some wierd reason chappy!
   <br/>
   <br/>I have implemented the auto save feature, but does it really work after 100s?
   <br/>
   </p> 
   <p> 
   <i>Autosave?? </i> 
   </p> 
   <p>we are talking...</p>
   **<p>**
   <hr/>
   <p>
   <br/>
   </p>
</body>

La etiqueta negrita p es la que no se imprimió correctamente ... ¿Hay alguna forma de evitar esto? ¿Estoy haciendo algo mal con el código?

Respuesta aceptada

Lo que estás tratando de hacer es básicamente transformar una entrada Html en una salida Xml.

Html Agility Pack puede hacer eso cuando usa la opción OptionOutputAsXml , pero en este caso, no debe usar la propiedad InnerHtml, y en su lugar deje que el Html Agility Pack haga el trabajo por usted, con uno de los métodos de Save de HtmlDocument.

Aquí hay una función genérica para convertir un texto HTML en una instancia de XElement:

public static XElement HtmlToXElement(string html)
{
    if (html == null)
        throw new ArgumentNullException("html");

    HtmlDocument doc = new HtmlDocument();
    doc.OptionOutputAsXml = true;
    doc.LoadHtml(html);
    using (StringWriter writer = new StringWriter())
    {
        doc.Save(writer);
        using (StringReader reader = new StringReader(writer.ToString()))
        {
            return XElement.Load(reader);
        }
    }
}

Como ves, no tienes que hacer mucho trabajo por tu cuenta. Tenga en cuenta que, dado que su texto de entrada original no tiene elemento raíz, el paquete de agilidad de Html agregará automáticamente un SPAN adjunto para garantizar que el resultado sea un Xml válido.

En su caso, desea procesar adicionalmente algunas etiquetas, así que aquí tiene cómo hacerlo con su ejemplo:

    public static XElement CleanupHtml(string input)
    {
        if (input == null)
            throw new ArgumentNullException("input");

        HtmlDocument doc = new HtmlDocument();
        doc.OptionOutputAsXml = true;
        doc.LoadHtml(input);

        // extra processing, remove some attributes using DOM
        HtmlNodeCollection coll = doc.DocumentNode.SelectNodes("//br[@class='special_class']");
        if (coll != null)
        {
            foreach (HtmlNode node in coll)
            {
                node.Attributes.Remove("class");
            }
        }

        using (StringWriter writer = new StringWriter())
        {
            doc.Save(writer);
            using (StringReader reader = new StringReader(writer.ToString()))
            {
                return XElement.Load(reader);
            }
        }
    }

Como puede ver, no debe usar la función de cadena sin formato, sino las funciones DOM de Html Agility Pack (SelectNodes, Agregar, Eliminar, etc.).


Respuesta popular

Si revisa los comentarios de la documentación para OptionFixNestedTags verá lo siguiente:

//     Defines if LI, TR, TH, TD tags must be partially fixed when nesting errors
//     are detected. Default is false.

Así que no creo que esto te ayude con las etiquetas p HTML sin cerrar. De acuerdo con una antigua biblioteca SO question C # para limpiar html, aunque HTML Tidy podría funcionar para este propósito.



Related

Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué
Licencia bajo: CC-BY-SA with attribution
No afiliado con Stack Overflow
¿Es esto KB legal? Sí, aprende por qué