HtmlAgilityPack sottostringa di tutto per lunghezza

c# html-agility-pack

Domanda

Ho html con elementi nidificati (principalmente solo elementi div e p ) Ho bisogno di restituire lo stesso html, ma sottostrato da un numero dato di lettere. Ovviamente il conteggio delle lettere non deve essere enumerato tramite i tag html, ma solo le lettere di conteggio di InnerText di ciascun elemento html. Il risultato dell'Html dovrebbe preservare la struttura corretta - qualsiasi tag di chiusura per rimanere in html valido.

Esempio di input:

<div>
    <p>some text</p>
    <p>some more text some more text some more text some more text some more text</p>
    <div>
        <p>some more text some more text some more text some more text some more text</p>
        <p>some more text some more text some more text some more text some more text</p>
    </div>
</div>

Dato int length = 16 l'output dovrebbe apparire così:

<div>
    <p>some text</p>
    <p>some more text some more text some more text some more text some more text</p>
    <div>
        <p>some more text some more text some more text some more text some more text</p>
        <p>some more text some more text some more text some more text some more text</p>
    </div>
</div>

Si noti che il numero di lettere (spazi inclusi) è 16. Il successivo <div> viene eliminato poiché il conteggio delle lettere ha raggiunto la length variabile. Si noti che l'output html è ancora valido.

Ho provato quanto segue, ma non funziona davvero. L'output non è come previsto: alcuni elementi HTML vengono ripetuti.

<div>
    <p>some text</p>
    <p>some more text some more text some more text some more text some more text</p>
    <div>
        <p>some more text some more text some more text some more text some more text</p>
        <p>some more text some more text some more text some more text some more text</p>
    </div>
</div>

AGGIORNARE

@SergeBelov ha fornito una soluzione che funziona per il primo input di esempio, tuttavia ulteriori test hanno presentato un problema con un input simile a quello riportato di seguito.

Esempio di input n. 2:

<div>
    <p>some text</p>
    <p>some more text some more text some more text some more text some more text</p>
    <div>
        <p>some more text some more text some more text some more text some more text</p>
        <p>some more text some more text some more text some more text some more text</p>
    </div>
</div>

Dato che la variabile int maxLength = 7; un'uscita dovrebbe essere uguale ad alcuni mo . Non funziona così a causa di questo codice in cui ParentNode = null :

<div>
    <p>some text</p>
    <p>some more text some more text some more text some more text some more text</p>
    <div>
        <p>some more text some more text some more text some more text some more text</p>
        <p>some more text some more text some more text some more text some more text</p>
    </div>
</div>

La creazione di un nuovo HtmlNode non sembra essere d'aiuto in quanto la sua proprietà InnterText è di sola lettura.

Risposta accettata

Il piccolo programma della console di seguito illustra un possibile approccio, che è:

  1. Seleziona nodi di testo rilevanti e calcola il totale parziale di lunghezza per loro;
  2. Prendi tutti i nodi necessari per raggiungere il totale corrente oltre la lunghezza massima;
  3. Rimuovi tutti i nodi degli elementi dal documento tranne quelli che sono antenati dei nodi che abbiamo selezionato durante i passaggi ## 1, 2;
  4. Taglia il testo nell'ultimo nodo dell'elenco per adattarlo alla lunghezza massima.

AGGIORNAMENTO: Questo dovrebbe ancora funzionare con un nodo di testo che è il primo; probabilmente, è necessario un Trim() per rimuovere gli spazi bianchi da esso come di seguito.

    static void Main(string[] args)
    {
        int maxLength = 9;
        string input = @"
            some more text some more text 
            <div>
                <p>some text</p>
                <p>some more text some more text some more text some more text some more text</
            </div>";

        var doc = new HtmlDocument();
        doc.LoadHtml(input);

        // Get text nodes with the appropriate running total
        var acc = 0;
        var nodes = doc.DocumentNode
            .Descendants()
            .Where(n => n.NodeType == HtmlNodeType.Text && n.InnerText.Trim().Length > 0)
            .Select(n => 
            {
                var length = n.InnerText.Trim().Length;
                acc += length;
                return new { Node = n, TotalLength = acc, NodeLength = length }; 
            })
            .TakeWhile(n => (n.TotalLength - n.NodeLength) < maxLength)
            .ToList();

        // Select element nodes we intend to keep
        var nodesToKeep = nodes
            .SelectMany(n => n.Node.AncestorsAndSelf()
                .Where(m => m.NodeType == HtmlNodeType.Element));

        // Select and remove element nodes we don't need
        var nodesToDrop = doc.DocumentNode
            .Descendants()
            .Where(m => m.NodeType == HtmlNodeType.Element)
            .Except(nodesToKeep)
            .ToList();

        foreach (var r in nodesToDrop)
            r.Remove();

        // Shorten the last node as required
        var lastNode = nodes.Last();
        var lastNodeText = lastNode.Node;
        var text = lastNodeText.InnerText.Trim().Substring(0,
                lastNode.NodeLength - lastNode.TotalLength + maxLength);
        lastNodeText
            .ParentNode
            .ReplaceChild(HtmlNode.CreateNode(text), lastNodeText);

        doc.Save(Console.Out);
    }



Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché
Autorizzato sotto: CC-BY-SA with attribution
Non affiliato con Stack Overflow
È legale questo KB? Sì, impara il perché