提问人:speci5 提问时间:6/10/2022 最后编辑:Janez Kuharspeci5 更新时间:6/12/2022 访问量:340
以递归方式遍历嵌套的 HTML 列表
Traversing nested HTML lists recursively
问:
我无法获取下面的 HTML 并将其放入如下列表中:
List<String> output = Arrays.asList(
new String[] {
"First Level-Second Level--Third Level",
"a-b--c1",
"a-b--c2",
"a-b--c3",
"a-b2--c1",
"a-b2--c2",
"a-b2--c3"
});
<ul>
<li>First Level</li>
<ul>
<li>Second Level</li>
<ul>
<li>Third Level</li>
</ul>
</ul>
<li>a</li>
<ul>
<li>b</li>
<ul>
<li>c</li>
<li>c</li>
<li>c</li>
</ul>
<li>b2</li>
<ul>
<li>c1</li>
<li>c2</li>
<li>c3</li>
</ul>
</ul>
</ul>
我已将其加载到 Jsoup 中以像元素一样遍历它们。我的代码如下:
public static String recHTML(Element element, String str) {
str += "-" + element.ownText();
if (element.children().size() == 0) return str + "--" + element.ownText();
else {
for (int i = 0; i < element.children().size(); i++) {
Element next = element.child(i);
str += recHTML(next, str);
}
}
}
我将返回带有破折号的字符串,以使用正则表达式将它们拆分为一个数组以具有不同级别的缩进。我正在努力让我的输出匹配,无论我尝试什么,我都无法让它工作。任何帮助将不胜感激,谢谢。
答:
1赞
Janez Kuhar
6/12/2022
#1
假设您创建了一个类似 bellow 的程序存根:
import java.util.ArrayList;
import java.util.List;
import org.jsoup.Jsoup;
import org.jsoup.nodes.Element;
public class App {
private static List<String> output = new ArrayList<String>();
public static void main(String[] args) {
Element input = Jsoup.parse(/*your HTML list*/).selectFirst("body").child(0);
recHTML(input, "", "");
System.out.println(output);
}
public static void recHTML(Element element, String chain, String link) {
// Todo!
}
}
现在让我们考虑一下无效的 HTML 示例及其有效的等效项
无效的 HTML 输入列表
<ul>
<li>First Level</li>
<ul>
<li>Second Level</li>
<ul>
<li>Third Level</li>
</ul>
</ul>
<li>a</li>
<ul>
<li>b</li>
<ul>
<li>c</li>
<li>c</li>
<li>c</li>
</ul>
<li>b2</li>
<ul>
<li>c1</li>
<li>c2</li>
<li>c3</li>
</ul>
</ul>
</ul>
目前,你正在尝试实现深度优先搜索 (DFS) 之类的功能。典型的DFS是行不通的。为什么?因为您提供的 HTML 结构具有多个不终止链的叶节点。
假设我们将你的每个元素称为链。当您以 DFS 方式遍历 HTML 树时,请注意,链是完整的
一旦你找到一个没有兄弟姐妹的元素。这应该是你的最终条件。至于递归步骤,请考虑如何处理和元素。定义并不容易;目前,我的解决方案仅适用于您的特定输入。output
li
ul
ul
li
你的实现注定是丑陋的,因为从根节点到叶节点没有直接的嵌套链。新的边缘情况将不断出现,您的代码将不起作用,您将继续进行编辑。recHTML
public static void recHTML(Element element, String chain, String link) {
if (element.parent().children().select("ul").isEmpty()) {
output.add(chain);
}
for (int i = 0; i < element.children().size(); i++) {
Element next = element.child(i);
if (next.tagName().equals("ul")) {
recHTML(next, chain + link + element.child(i - 1).ownText(), link + '-');
} else {
recHTML(next, chain + link + next.ownText(), link);
}
}
}
有效的 HTML 输入列表
<ul>
<li>
First level
<ul>
<li>
Second Level
<ul>
<li>Third Level</li>
</ul>
</li>
</ul>
</li>
<li>
a
<ul>
<li>
b
<ul>
<li>c</li>
<li>c</li>
<li>c</li>
</ul>
</li>
<li>
b2
<ul>
<li>c1</li>
<li>c2</li>
<li>c3</li>
</ul>
</li>
</ul>
</li>
</ul>
正如我的评论中所建议的,您的问题出在输入中。假设我们以某种方式将您的输入 HTML 转换为上述标记。然后解决方案变得更干净、更直接 - 一个简单的 DFS 就可以了!
public static void recHTML(Element element, String chain, String link) {
if (element.children().isEmpty()) {
output.add(chain);
}
for (Element child : element.children()) {
if (child.tagName().equals("li")) {
recHTML(child, chain + link + child.ownText(), link + "-");
} else {
recHTML(child, chain, link);
}
}
}
有趣的事实:Java HTML 整洁工具 JTidy 以与我略有不同的方式更正了您的 HTML 输入:
<!-- original snippet -->
<li>First Level</li>
<ul>
<li>Second Level</li>
<!-- omitted elements for brevity -->
</ul>
<!-- snippet, corrected by jtidy -->
<li>First Level</li>
<li style="list-style: none">
<ul>
<li>Second Level</li>
<!-- omitted elements for brevity -->
</ul>
</li>
就个人而言,我不认为它以正确的方式纠正它 - 制作 HTML 嵌套列表的正确方法?
评论
recHTML
<ul><ul><li>a</li></ul><li>b</li></ul>