Объединение одинаковых элементов в JSoup - PullRequest
0 голосов
/ 16 марта 2020

У меня есть строка 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), но чем как свернуть элементы?

Или существует простое слияние регулярных выражений?

Атрибуты, которые я могу указать самостоятельно. Нет необходимости иметь универсальное решение для тегов.

Ответы [ 2 ]

1 голос
/ 17 марта 2020

Мой подход был бы таким. Комментарии в коде

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>

Еще одно примечание о том, почему я использовал l oop while (nextSibling.childNodes().size() > 0). Оказалось, что for или iterator не может быть использовано здесь, потому что appendChild добавляет дочерний элемент, но удаляет его из исходного элемента, а оставшиеся дети перемещаются. Это может быть не видно здесь, но проблема появится, когда вы попытаетесь объединить: <b>test</b><b>er<a>123</a></b>

0 голосов
/ 18 марта 2020

Я пытался обновить код из @Krystian G, но мое редактирование было отклонено: - / Поэтому я публикую его как собственный пост. Код является отличной отправной точкой, но он терпит неудачу, если между тегами появляется TextNode, например,

<span> no class but further</span> (in)valid <span>spanning</span> приведет к

<span> no class but furtherspanning</span> (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();
      }
    }
  }
}
...