Ich habe ein großes Dokument von Word nach HTML konvertiert. Es ist nah dran, aber ich habe eine Reihe von "Code" -Knoten, die ich zu einem "Pre" -Knoten zusammenführen möchte.
Hier ist die Eingabe:
<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>
Ich möchte es stattdessen so machen:
<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>
Ich habe am Ende eine Brute-Force-Schleife geschrieben, die Knoten durchläuft, die nach aufeinanderfolgenden suchen, aber das scheint mir hässlich zu sein:
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();
}
Normalerweise würde ich die Knoten in der ersten Schleife entfernen, aber das funktioniert während der Iteration nicht, da Sie die Auflistung nicht ändern können, während Sie darüber iterieren.
Bessere Ideen?
Der erste Ansatz: Wählen Sie alle <code>
-Knoten aus, gruppieren Sie sie und erstellen Sie einen <pre>
-Knoten pro Gruppe:
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();
}
Das Gruppierungsfeld ist hier ein kombiniertes Feld eines Elternknotens und Gruppenindexes, das erhöht wird, wenn eine neue Gruppe gefunden wird. Auch hier habe ich die NextSiblingIsCode
Erweiterungsmethode verwendet:
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");
}
Es wurde festgestellt, ob das nächste Geschwister ein <code>
-Knoten ist.
<code>
-Knoten jeder Gruppe aus, und durchlaufen Sie dann jeden dieser Knoten, um den nächsten <code>
-Knoten bis zum ersten nicht <code>
-Knoten zu finden. Ich xpath
hier 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())
));
}