HtmlAgilityPack:html要素をクラスと一緒に1つのタグに結合するにはどうすればいいですか?

html-agility-pack vb.net

質問

問題: HtmlAgilityPackを使用していくつかのHTML要素を調べ、タグ名を組み合わせる必要があります。親から子への各タグを抽出して、 "strikeUEmStrong"という名前のクラスを持つスパンに置き換えることは可能ですか?また、名前はHTML要素に基づいて変更されます。

クラスの名前の順番は実際問題ですが、私は試行錯誤でこれを認識しました。限り、すべての要素を取得し、それらを組み合わせることができる限り。さまざまなレベルの書式設定を持つ複数のテキストノードを持つことは非常に可能です。

これは複数の段落に影響します。

たとえば、私はこのhtmlコードを持っています:

<p>
<strike><u><em><strong>four styles</strong></em></u></strike></p>

どのように私はこれに変換する:

<p>
<span class="strikeUEmStrong">four styles</span></p>

このタイプのコードを持つことも可能です:

<p>
    <strike><u><em><strong>four styles</strong></em></u></strike>&nbsp; <strike><u><em>three styles</em></u></strike></p>
<p>
    <em><strong>two styles</strong></em></p>

出力は次のようになります。

<p>
<span class="strikeUEmStrong">four styles</span>&nbsp; <span class="strikeUEm">three styles<span></p><p><span class="emStrong">two styles<span></p>

プロトタイプ:

'Retrive the class name of each format node
Function GetClassName(ByVal n As HtmlNode) As String
    Dim ret As String = String.Empty

    If (n.Name <> "#text") And (n.Name <> "p") Then
        ret = n.Name + " "
    End If

    'Get the next node
    For Each child As HtmlNode In n.ChildNodes
        ret &= GetClassName(child)
    Next

    Return ret
End Function

'Create a list of class names
Function GetClassNameList(ByVal classNameList As String) As List(Of String)
    Dim ret As New List(Of String)
    Dim classArr() As String = classNameList.Split(" ")

    For Each className As String In classArr
        ret.Add(className)
    Next

    Return ret
End Function

'Sort a list of class names and return a merged class string
Function GetSortedClassNameString(ByVal classList As List(Of String)) As String

    Dim sortedMergedClass As String = String.Empty

    classList.Sort()

    For Each className As String In classList
        sortedMergedClass &= className
    Next

    Return sortedMergedClass
End Function

'Lets point to the body node
Dim bodyNode As HtmlNode = htmlDoc.DocumentNode.SelectSingleNode("//body")

'Lets create some generic nodes
Dim currPNode As HtmlNode

Dim formatNodes As HtmlNodeCollection

Dim text As String = String.Empty
Dim textSize As Integer = 0

'Make sure the editor has something in it
If editorText <> "" Then

   'Send the text from the editor to the body node
    If bodyNode IsNot Nothing Then
       bodyNode.InnerHtml = editorText
    End If

    Dim pNode = bodyNode.SelectNodes("//p")

    Dim span As HtmlNode = htmlDoc.CreateElement("span")
    Dim tmpBody As HtmlNode = htmlDoc.CreateElement("body")
    Dim textNode As HtmlNode = htmlDoc.CreateTextNode

    Dim pCount As Integer = bodyNode.SelectNodes("//body/p").Count - 1

    For childCountP As Integer = 0 To pCount

        Dim paragraph = HtmlNode.CreateNode(htmlDoc.CreateElement("p").WriteTo)

        'Which paragraph I am at.
        currPNode = pNode.Item(childCountP)

        'For this paragraph get me the collection of html nodes
        formatNodes = currPNode.ChildNodes

        'Count how many Format nodes we have in a paragraph
        Dim formatCount As Integer = currPNode.ChildNodes.Count - 1

       'Go through each node and examine the elements. 
       'Then look at the markup to create classes and then group them under one span
       For child As Integer = 0 To formatCount

           'Iterate through the formateNodes, strike, em, strong, etc.
           Dim currFormatNode = HtmlNode.CreateNode(formatNodes(child).WriteTo)

           'TODO: Handle nested images and links? How do we know what to rip out?

           'First check for format nodes
           'Note, we can't let it use everything because it will change nested elements as well. I.E. span within span.
           If (currFormatNode.Name <> "#text") And (currFormatNode.Name = "strike") Or (currFormatNode.Name = "em") _
               Or (currFormatNode.Name = "strong") Or (currFormatNode.Name = "u") Or (currFormatNode.Name = "sub") _
               Or (currFormatNode.Name = "sup") Or (currFormatNode.Name = "b") Then

              'strip all tags, just take the inner text
              span.InnerHtml = currFormatNode.InnerText

              'Create a text node with text from the lowest node
              textNode = htmlDoc.CreateTextNode(span.InnerText)

              'Recursively go through the format nodes
              'Create a list from the string
              'Then sort the list and return a string
              'Appending the class to the span
               span.SetAttributeValue("class", GetSortedClassNameString(GetClassNameList(GetClassName(currFormatNode).Trim())))

              'Attach the span before the current format node
              currFormatNode.ParentNode.InsertBefore(span, currFormatNode)

             'Remove the formatted children leaving the above node
             currFormatNode.ParentNode.ChildNodes.Remove(currFormatNode)

             'We need to build a paragraph here
             paragraph.InnerHtml &= span.OuterHtml

             'Lets output something for debugging
             childNodesTxt.InnerText &= span.OuterHtml

             Else 'handle #text and other nodes seperately
                  'We need to build a paragraph here
                  paragraph.InnerHtml &= span.OuterHtml
                  textNode = htmlDoc.CreateTextNode(currFormatNode.InnerHtml)

                  'Lets output something for debugging
                  childNodesTxt.InnerText &= textNode.OuterHtml
             End If

        Next
        'End of formats

        'Start adding the new paragraph's to the body node
        tmpBody.AppendChild(paragraph)
     Next
     'End of paragraphs

    'Clean out body first and replace with new elements
    htmlDoc.DocumentNode.SelectSingleNode("//body").Remove()

    'Update our body
    htmlDoc.DocumentNode.SelectSingleNode("//html").AppendChild(tmpBody)

 End If

 htmlDoc.Save(Server.MapPath("html\editor.html"))
 End If

出力:

<span class="strikeuemstrong">four styles</span>

最終的に正しい出力を得るために、私は注文の問題を修正した。お手伝いありがとう。

受け入れられた回答

これは正直な答えではありません。私はこれを行うためにアルゴリズムを書く方法を説明し、擬似コードを含めて説明します。

  1. 私は親のタグを手に入れた。私はあなたがすべての "p"タグに対してこれをしたいと仮定します
  2. 私は子供のタグを繰り返し、タグ名をつけてクラス名に追加します
  3. 私は追加されたタグ名が得られるまで再帰的に子を繰り返します

擬似コード。私はこれをタイピングしているので、間違いはありません。

public string GetClassName(Node n)
{
var ret = n.TagName;

foreach(var child in n.ChildNodes)
{
ret += GetClassName(child);
}

return ret;
}


foreach(var p in paragraphs)
{
foreach(var child in p.ChildNodes)
{
 var span = new Span();
 span.InnerText = child.InnerText; // strip all tags, just take the inner text

span.ClassName = GetClassName(child);

child.ReplaceWith(span); // note: if you do this with a FOREACH and not a for loop, it'll blow up C# for modifying the collection while iterating.  Use for loops. if you're going to do "active" replacement like in this pseudo code
}
}

より多くの文脈を得ると、私は自分の答えを修正することができて嬉しいです。あなたが私の提案を洗練させる必要がある場合は、私が提案していることをレビューし、より多くの文脈でそれについてコメントしてください。そうでない場合は、これがあなたに必要なものを得ることを願っています:)




ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ
ライセンスを受けた: CC-BY-SA with attribution
所属していない Stack Overflow
このKBは合法ですか? はい、理由を学ぶ