javajsoup

Jsoup 1.19.1 StreamParser duplicating hrefs in a valid html documents


On using StreamParser.parse(), it is found that hrefs links are duplicated , whereas using Jsoup.parse() returns the expected document. Is there any reason why StreamParser would create additional references to href?

Is this expected behaviour when using a StreamParser?

String s1= "<!DOCTYPE html>\n"
                + "<html>\n"
                + "<head>\n"
                + "  <title></title>\n"
                + "</head>\n"
                + "<body>\n"
                + "  <a href=\"https://fake.com/:x:/g/gibberishtext\">Some link</a>\n"
                + "</body>\n"
                + "</html>";

  StreamParser streamParser = new StreamParser(Parser.htmlParser());
  StreamParser parse = streamParser.parse(s1, "");
  parse.stream().forEach(System.out::println);

// Output from StreamParser, Returns five references to href

<title></title>
<head>
 <title></title>
</head>
<a href="https://fake.com/:x:/g/gibberishtext">Some link</a>
<body>
 <a href="https://fake.com/:x:/g/gibberishtext">Some link</a>
</body>
<a href="https://fake.com/:x:/g/gibberishtext">Some link</a>
<body>
 <a href="https://fake.com/:x:/g/gibberishtext">Some link</a>
</body>
<html>
 <head>
  <title></title>
 </head>
 <body>
  <a href="https://fake.com/:x:/g/gibberishtext">Some link</a>
 </body>
</html>
<!doctype html>
<html>
 <head>
  <title></title>
 </head>
 <body>
  <a href="https://fake.com/:x:/g/gibberishtext">Some link</a>
 </body>
</html>

Whereas using Jsoup.parse() returns the expected document

 Document parse1 = Jsoup.parse(s1);
 System.out.println(parse1.toString());

//Output from using Jsoup 1.19.1

<!doctype html>
<html>
 <head>
  <title></title>
 </head>
 <body>
  <a href="https://fake.com/:x:/g/gibberishtext">Some link</a>
 </body>
</html>

Further update....

Now to make it more interesting..

If I call Jsoup.parse(s1).stream().forEach(System.out::println) it gives the result similar to StreamParser.

Why calling stream() is causing duplication?


Solution

  • That's an interesting question! There are in fact two kinds of duplications going on.

    1st Duplication

    First of, when you print an element, you also print all of its children. That makes your output a bit verbose. Perhaps it'd be more prudent to replace:

    parse.stream().forEach(System.out::println);
    

    with something like:

    parse.stream().forEach(el -> System.out.println(el.tagName()));
    

    to only print element tags.

    2nd Duplication

    Fixing 1, you'd then observe the following output:

    title
    head
    a
    body
    a
    body
    html
    #root
    

    There's still some duplication but not nearly as much. Let's first focus on the output order. With StreamParser...

    Elements are emitted as they are completed... [Source]

    This means that whenever a full element is parsed, it will be emitted. It's easy to check that title is the first element that's fully parsed.

    However, the following sequence:

    a
    body
    

    appearing twice in the output seems wrong. One would expect only one occurrence. I can't say whether that's intended behavior or a bug.

    But if I replace:

    StreamParser streamParser = new StreamParser(Parser.htmlParser());
    

    with:

    StreamParser streamParser = new StreamParser(Parser.xmlParser());
    

    I get the output I'd expect:

    title
    head
    a
    body
    html
    #root