【问题标题】:Merging same elements in JSoup在 JSoup 中合并相同的元素
【发布时间】:2020-03-16 10:53:18
【问题描述】:

我有类似的 HTML 字符串

<b>test</b><b>er</b>
<span class="ab">continue</span><span> without</span>

我想折叠相似且属于彼此的标签。在上面的示例中,我想要

<b>tester</b>

因为标签具有相同的标签,没有任何进一步的属性或样式。但是对于span 标签,它应该保持不变,因为它有一个class 属性。我知道我可以通过 Jsoup 在树上进行迭代。

Document doc = Jsoup.parse(input);
for (Element element : doc.select("b")) {
}

但我不清楚如何向前看(我猜像nextSibling),而不是如何折叠元素?

或者存在一个简单的正则表达式合并?

我可以自己指定的属性。不需要万能的标签解决方案。

【问题讨论】:

    标签: java jsoup


    【解决方案1】:

    我的方法是这样的。代码中的注释

    public class StackOverflow60704600 {
    
        public static void main(final String[] args) throws IOException {
            Document doc = Jsoup.parse("<b>test</b><b>er</b><span class=\"ab\">continue</span><span> without</span>");
            mergeSiblings(doc, "b");
            System.out.println(doc);
    
        }
    
        private static void mergeSiblings(Document doc, String selector) {
            Elements elements = doc.select(selector);
            for (Element element : elements) {
                // get the next sibling
                Element nextSibling = element.nextElementSibling();
                // merge only if the next sibling has the same tag name and the same set of attributes
                if (nextSibling != null && nextSibling.tagName().equals(element.tagName())
                        && nextSibling.attributes().equals(element.attributes())) {
                    // your element has only one child, but let's rewrite all of them if there's more
                    while (nextSibling.childNodes().size() > 0) {
                        Node siblingChildNode = nextSibling.childNodes().get(0);
                        element.appendChild(siblingChildNode);
                    }
                    // remove because now it doesn't have any children
                    nextSibling.remove();
                }
            }
        }
    }
    

    输出:

    <html>
     <head></head>
     <body>
      <b>tester</b>
      <span class="ab">continue</span>
      <span> without</span>
     </body>
    </html>
    

    关于我为什么使用循环 while (nextSibling.childNodes().size() &gt; 0) 的另一个说明。原来foriterator 不能在此处使用,因为appendChild 添加了子元素,但将其从源元素中删除,剩余的子元素被移动。此处可能看不到,但尝试合并时会出现问题:&lt;b&gt;test&lt;/b&gt;&lt;b&gt;er&lt;a&gt;123&lt;/a&gt;&lt;/b&gt;

    【讨论】:

    • 嗨,我已经更新了代码,因为如果其他节点彼此相邻,这在意义上是不完全正确的。我不知道现在是否所有案件都得到妥善处理——但我想是的。另外我不知道Jsoup 是否允许两个连续的TextNode 相邻。我猜不是——因此我只是添加了一个简单的检查。
    • 除非您发现更多可能有问题的测试用例,否则我将保留它并稍后接受您的解决方案。非常感谢您的帮助!
    【解决方案2】:

    我尝试从@Krystian G 更新代码,但我的编辑被拒绝:-/因此我将其发布为自己的帖子。该代码是一个很好的起点,但如果在标签之间出现 TextNode 则会失败,例如

    &lt;span&gt; no class but further&lt;/span&gt; (in)valid &lt;span&gt;spanning&lt;/span&gt; 将导致

    &lt;span&gt; no class but furtherspanning&lt;/span&gt; (in)valid

    因此修正后的代码如下:

    public class StackOverflow60704600 {
    
        public static void main(final String[] args) throws IOException {
            String test1="<b>test</b><b>er</b><span class=\"ab\">continue</span><span> without</span>";
            String test2="<b>test</b><b>er<a>123</a></b>";
            String test3="<span> no class but further</span>   <span>spanning</span>";
            String test4="<span> no class but further</span> (in)valid <span>spanning</span>";
            Document doc = Jsoup.parse(test1);
            mergeSiblings(doc, "b");
            System.out.println(doc);
        }
    
     private static void mergeSiblings(Document doc, String selector) {
        Elements elements = doc.select(selector);
        for (Element element : elements) {
          Node nextElement = element.nextSibling();
          // if the next Element is a TextNode but has only space ==> we need to preserve the
          // spacing
          boolean addSpace = false;
          if (nextElement != null && nextElement instanceof TextNode) {
            String content = nextElement.toString();
            if (!content.isBlank()) {
              // the next element has some content
              continue;
            } else {
              addSpace = true;
            }
          }
          // get the next sibling
          Element nextSibling = element.nextElementSibling();
          // merge only if the next sibling has the same tag name and the same set of
          // attributes
          if (nextSibling != null && nextSibling.tagName().equals(element.tagName())
              && nextSibling.attributes().equals(element.attributes())) {
            // your element has only one child, but let's rewrite all of them if there's more
            while (nextSibling.childNodes().size() > 0) {
              Node siblingChildNode = nextSibling.childNodes().get(0);
              if (addSpace) {
                // since we have had some space previously ==> preserve it and add it
                if (siblingChildNode instanceof TextNode) {
                  ((TextNode) siblingChildNode).text(" " + siblingChildNode.toString());
                } else {
                  element.appendChild(new TextNode(" "));
                }
              }
              element.appendChild(siblingChildNode);
            }
            // remove because now it doesn't have any children
            nextSibling.remove();
          }
        }
      }
    }
    

    【讨论】:

      猜你喜欢
      • 2023-03-29
      • 2020-10-20
      • 1970-01-01
      • 2013-05-31
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多