从 HTML header 标签创建树 object-structure
Create a tree object-structure from HTML header tags
从 HTML header 标签创建树 object-structure 的最佳方法是什么? (我最关心的是之后的可读性)
这是 object 我想将 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': []
}]
好吧,这一点都不漂亮,如果 HTML
的结构变化太多,它可能会中断,但它看起来很有效,也许它会让您知道从哪里开始。
首先,我会将 属性 public int Level { get; set; }
添加到您的 Node
class 以简化操作。
接下来,您可能需要一种方法来判断 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;
}
算法类似于:
- 获取第一个标题并将其设置为当前节点
- 如果下一个元素不是标题,则将其内容附加到当前节点
- 重复最后一步,直到下一个元素是标题。
- 将当前节点设置为parent节点,获取下一个标题并将其设置为新的当前节点。
- 如果下一个标题有更高级别,请将其添加到 parent。
- 如果级别相同或更低,则找到级别更低的最后一个节点,并将其添加到该节点。
- 如果没有下级节点,则视为"first heading"。
- 重复
像这样:
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>
,它也可能是为了让它起作用。修正了那个。
从 HTML header 标签创建树 object-structure 的最佳方法是什么? (我最关心的是之后的可读性)
这是 object 我想将 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': []
}]
好吧,这一点都不漂亮,如果 HTML
的结构变化太多,它可能会中断,但它看起来很有效,也许它会让您知道从哪里开始。
首先,我会将 属性 public int Level { get; set; }
添加到您的 Node
class 以简化操作。
接下来,您可能需要一种方法来判断 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;
}
算法类似于:
- 获取第一个标题并将其设置为当前节点
- 如果下一个元素不是标题,则将其内容附加到当前节点
- 重复最后一步,直到下一个元素是标题。
- 将当前节点设置为parent节点,获取下一个标题并将其设置为新的当前节点。
- 如果下一个标题有更高级别,请将其添加到 parent。
- 如果级别相同或更低,则找到级别更低的最后一个节点,并将其添加到该节点。
- 如果没有下级节点,则视为"first heading"。
- 重复
像这样:
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>
,它也可能是为了让它起作用。修正了那个。