Html Agility Pack과 노드를 결합하는 가장 좋은 방법

c# html-agility-pack

문제

큰 문서를 Word에서 HTML로 변환했습니다. 그것은 가깝지만, 하나의 "사전"노드로 병합하고자하는 "코드"노드가 많이 있습니다.

입력 내용은 다음과 같습니다.

<p>Here's a sample MVC Controller action:</p>
<code>        public ActionResult Index()</code>
<code>        {</code>
<code>            return View();</code>
<code>        }</code>
<p>We'll start by making the following changes...</p>

대신 이것을 다음과 같이 바꾸고 싶습니다.

<p>Here's a sample MVC Controller action:</p>
<pre class="brush: csharp">        public ActionResult Index()
    {
        return View();
    }</pre>
<p>We'll start by making the following changes...</p>

나는 연속 된 노드를 찾는 노드를 반복하는 무차별 대항 루프를 작성했다. 그러나 이것은 나에게보기 흉하게 보인다.

HtmlDocument doc = new HtmlDocument();
doc.Load(file);

var nodes = doc.DocumentNode.ChildNodes;
string contents = string.Empty;

foreach (HtmlNode node in nodes)
{

    if (node.Name == "code")
    {
        contents += node.InnerText + Environment.NewLine;
        if (node.NextSibling.Name != "code" && 
            !(node.NextSibling.Name == "#text" && node.NextSibling.NextSibling.Name == "code")
            )
        {
            node.Name = "pre";
            node.Attributes.RemoveAll();
            node.SetAttributeValue("class", "brush: csharp");
            node.InnerHtml = contents;
            contents = string.Empty;
        }
    }
}

nodes = doc.DocumentNode.SelectNodes(@"//code");
foreach (var node in nodes)
{
    node.Remove();
}

일반적으로 첫 번째 루프에서는 노드를 제거하지만 반복을 수행하는 동안 컬렉션을 변경할 수 없으므로 반복 중에는 작동하지 않습니다.

더 나은 아이디어?

인기 답변

첫 번째 접근법 : 모든 <code> 노드를 선택하고 그룹화하고 그룹당 <pre> 노드를 만듭니다.

var idx = 0;
var nodes = doc.DocumentNode
    .SelectNodes("//code")
    .GroupBy(n => new { 
        Parent = n.ParentNode, 
        Index = n.NextSiblingIsCode() ? idx : idx++ 
    });

foreach (var group in nodes)
{
    var pre = HtmlNode.CreateNode("<pre class='brush: csharp'></pre>");
    pre.AppendChild(doc.CreateTextNode(
        string.Join(Environment.NewLine, group.Select(g => g.InnerText))
    ));
    group.Key.Parent.InsertBefore(pre, group.First());

    foreach (var code in group)
        code.Remove();
}

여기서 그룹화 필드는 부모 노드의 필드와 새로운 그룹이 발견 될 때 증가되는 그룹 인덱스의 조합 필드입니다. 또한 여기에 NextSiblingIsCode 확장 메서드를 사용했습니다.

public static bool NextSiblingIsCode(this HtmlNode node)
{
    return (node.NextSibling != null && node.NextSibling.Name == "code") ||
        (node.NextSibling is HtmlTextNode && 
         node.NextSibling.NextSibling != null && 
         node.NextSibling.NextSibling.Name == "code");
}

다음 형제가 <code> 노드인지 여부를 확인하는 데 사용됩니다.


두 번째 방법 : 상단에만 선택 <code> 그 다음 찾으려면 다음 각 노드를 반복, 각 그룹의 노드를 <code> 첫 번째 비까지 노드 <code> 노드를. 여기에 xpath 사용했습니다.

var nodes = doc.DocumentNode.SelectNodes(
    "//code[name(preceding-sibling::*[1])!='code']"
);
foreach (var node in nodes)
{
    var pre = HtmlNode.CreateNode("<pre class='brush: csharp'></pre>");
    node.ParentNode.InsertBefore(pre, node);
    var content = string.Empty;
    var next = node;
    do
    {
        content += next.InnerText + Environment.NewLine;
        var previous = next;
        next = next.SelectSingleNode("following-sibling::*[1][name()='code']");
        previous.Remove();
    } while (next != null);
    pre.AppendChild(doc.CreateTextNode(
        content.TrimEnd(Environment.NewLine.ToCharArray())
    ));
}


아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.
아래 라이선스: CC-BY-SA with attribution
와 제휴하지 않음 Stack Overflow
이 KB는 합법적입니까? 예, 이유를 알아보십시오.