Заменить строку на jsoup только в текстовых частях

Я нашел несколько тем с похожими вопросами и ценными ответами, но я все еще борюсь с этим:

Я хочу разобрать некоторый html с Jsoup, чтобы я мог заменить, например,

"changeme"

С

<changed>changeme</changed>

, но только если он отображается в текстовой части html, нет, если он является частью тега. Итак, начиная с этого html:

<body>
<p><a href="http://changeme.html">test changeme app</a></p>
</BODY>
</HTML>

Я хочу сделать так:

<body>
<p><a href="http://changeme.html">test <changed>changeme</changed> app</a></p>
</BODY>
</HTML>

Я пробовал несколько подходов, это то, что приближает меня к желаемому результату:

Document doc = null;
try {
    doc = Jsoup.parse(new File("tmp1450348256397.txt"), "UTF-8");
} catch (Exception ex) {
}

Elements els = doc.body().getAllElements();
for (Element e : els) {
    if (e.text().contains("changeme")) {
        e.html(e.html().replaceAll("changeme","<changed>changeme</changed>"));
    }
}
html = doc.toString();
System.out.println(html);

но при таком подходе я нахожу две проблемы:

<body>
<p><a href="http://<changed>changeme</changed> .html">test
    <changed>
        changeme
    </changed> 
app</a></p>
</BODY>
</HTML>
  1. разрывы строк вставляются до и после нового элемента, который я представляю. Это не настоящая проблема, так как я могу избавиться от них, если я использую #changed# для замены и после doc.toString () я снова заменяю их на нужное значение (с).

  2. проблема: URL-адрес в href был изменен, и я не хочу, чтобы это произошло.

идеи? Тнх.

3 ответов


вот мое решение:

String html=""
    +"<p><a href=\"http://changeme.html\">"
    +   "test changeme "
    +   "<div class=\"changeme\">"
    +     "inner text changeme"
    +   "</div>"
    +   " app</a>"
    +"</p>";
Document doc = Jsoup.parse(html);
Elements els = doc.body().getAllElements();
for (Element e : els) {
    List<TextNode> tnList = e.textNodes();
    for (TextNode tn : tnList){
        String orig = tn.text();
        tn.text(orig.replaceAll("changeme","<changed>changeme</changed>")); 
    }
}

html = doc.toString();
System.out.println(html);

TextNodes всегда являются листовыми узлами, т. е. они не содержат больше HTML-элементов. В вашем оригинальном подходе вы заменяете HTML элемента новым HTML на replaced changme строки. Вы только проверяете, что changeme является частью содержимого TextNodes, но заменяете каждое вхождение в HTML-строке элемента, включая все вхождения вне TextNodes.

мое решение в основном работает как ваше, но я использую Способ JSoup textNodes(). Таким образом, мне не нужно печатать.

С. П. Конечно, мое решение, как и ваше, будет содержать &lt;changed&gt;changeme&lt;/changed&gt; вместо <changed>changeme</changed> в конце. Это может быть или не быть тем, чего вы хотите. Если вы этого не хотите, то ваш результат больше не является допустимым HTML, так как changed не является допустимым тегом HTML. Jsoup не поможет вам в этом деле. Тем не менее, вы можете, конечно, заменить в результирующей строке все &lt;changed&gt;changeme&lt;/changed&gt; снова-вне JSoup.


Я думаю, ваша проблема в том, что вы заменяете элементы html, а не только его текст, измените:

e.html(e.html().replaceAll("changeme","<changed>changeme</changed>"));

до

e.text(e.text().replaceAll("changeme","<changed>changeme</changed>"));

проблема разрывов линии, вероятно, может быть решена путем выполнения doc.outputSettings().prettyPrint(false); перед html = doc.toString();


наконец, я попробовал это решение (в конце вопроса), используя TextNodes:

как я могу заменить "текст" в каждом теге с помощью Jsoup

Это результирующий код:

Elements els = doc.body().getAllElements();
for (Element e : els) {
    for (Node child : e.childNodes()){
        if (child instanceof TextNode && !((TextNode) child).isBlank()) {
            ((TextNode)child).text(((TextNode)child).text().replaceAll("changeme","<changed>changeme</changed>"));
        }
    }
}   

Теперь выход является ожидаемым, и он даже не вводит дополнительные линии разрыва. В этом случае prettyPrint должно быть установлено значение True.

единственная проблема в том, что я действительно не понимаю разницу в использовании TextNode vs Element.text(). Если кто-то хочет предоставить некоторую информацию, она будет очень признательна.

спасибо.