从HTML标头标签创建树对象结构的最佳方法是什么? (之后我最关心的是可读性)
以下是我想将HTML映射到的对象:
public class Node
{
public string Title { get; set; }
public string Html { get; set; }
public List<Node> SubNodes { get; set; }
}
以下是HTML示例:
<h1>Header 1</h1>
<p>Content under header 1</p>
<h2>H2 for header 1</h2>
<p>Content under H2 for header 1</p>
<h3>H3 for H2 under header 1</h3>
<h4>h4 for h3 under h2 and header 1<h4>
<p>Content under H4 for h3 and H2 under header 1</p>
<h2>Second H2 for header1</h2>
<p>Content under second H2 for header 1</p>
<h1>Second header 1</h1>
<p>Content under second header 1</p>
预期的结构看起来像这样(用JSON编写):
[{
'Title': 'Header 1',
'Html': '<h1>Header 1</h1><p>Content under header 1</p>',
'SubNodes': [{
'Title': 'H2 for header 1',
'Html': '<h2>H2 for header 1</h2><p>Content under H2 for header 1</p>',
'SubNodes': [{
'Title': 'H3 for H2 under header 1',
'Html': '<h3>H3 for H2 under header 1</h3>',
'SubNodes': [{
'Title': 'h4 for h3 under h2 and header 1,'
'Html': '<h4>h4 for h3 under h2 and header 1<h4><p>Content under H4 for h3 and H2 under header 1</p>',
'SubNodes': []
}]
},{
'Title': 'Second H2 for header1',
'Html': '<h2>Second H2 for header1</h2><p>Content under second H2 for header 1</p>',
'SubNodes': []
}]
}]
},{
'Title': 'Second header 1',
'Html': '<h1>Second header 1</h1><p>Content under second header 1</p>',
'SubNodes': []
}]
答案 0 :(得分:1)
嗯,这根本不是很好,如果HTML
的结构变化太大,可能会破坏,但似乎是工作,也许它会让你知道从哪里开始。
首先,我将属性public int Level { get; set; }
添加到您的Node
课程,以简化操作。
接下来,您可能想要一种方法来判断标题有哪个Level
。
我做了这样的事情:
bool IsHeading(string tagName, out int? level)
{
level = null;
if (tagName.StartsWith("h", StringComparison.OrdinalIgnoreCase) == false)
{
return false;
}
int tempLevel;
if (int.TryParse(tagName.Substring(1), out tempLevel) == false)
{
return false;
}
level = tempLevel;
return true;
}
然后算法类似于:
这样的事情:
var nodeList = new List<Node>();
var allNodes = new List<Node>();
Node parentNode = null;
Node currentNode = null;
foreach (var htmlNode in body.ChildNodes)
{
int? level;
if (IsHeading(htmlNode.Name, out level) && level.HasValue)
{
currentNode = new Node();
currentNode.Title = htmlNode.InnerText;
currentNode.Html = htmlNode.OuterHtml;
currentNode.Level = level.Value;
allNodes.Add(currentNode);
if (!allNodes.Any(n => n.Level < currentNode.Level))
{
nodeList.Add(currentNode);
parentNode = null;
}
if (parentNode != null)
{
if (parentNode.Level >= currentNode.Level)
{
parentNode = allNodes.Last(n => n.Level < currentNode.Level);
}
parentNode.SubNodes.Add(currentNode);
}
parentNode = currentNode;
continue;
}
if (currentNode == null)
{
continue;
}
currentNode.Html += htmlNode.OuterHtml;
}
再一次,不是自豪,而是从一开始就有所作为。
编辑1:不知道rootNode
是什么。不需要;除去。
编辑2:哦,这可能是为了让它发挥作用,即使第一个标题不是<h1>
。修好了。