Skip to content

Pretty-print XML responses#450

Merged
ducaale merged 7 commits intoducaale:masterfrom
o1x3:xml-pretty-print
Feb 24, 2026
Merged

Pretty-print XML responses#450
ducaale merged 7 commits intoducaale:masterfrom
o1x3:xml-pretty-print

Conversation

@o1x3
Copy link
Contributor

@o1x3 o1x3 commented Feb 17, 2026

Closes #231

Uses quick-xml to parse and re-indent XML response bodies. Works for both buffered and streaming (--stream) output.

quick-xml 0.38 is already in the dependency tree (via plist/syntect), so this doesn't add a new crate to the lockfile.

How it works

Reads XML events with trim_text(false) to preserve meaningful whitespace (e.g. <p>Hello <b>world</b></p>), but skips whitespace-only text nodes so that already-indented input gets re-indented without doubling up.

For the streaming+color path, a small HighlightingWriter adapter bridges quick-xml's event-based output with syntect's line-based highlighting. quick-xml writes formatted bytes and calls flush() after each event, and the adapter highlights and forwards them to the terminal on each flush.

Invalid XML falls back to syntax-highlight-only, same as how invalid JSON is handled.

Format options

  • --format-options=xml.format:false disables formatting
  • --format-options=xml.indent:2 changes indent (default 4, matching JSON)

These were already recognized by the CLI but rejected as "Unsupported".

Not included

HTML pretty-printing. HTML needs error recovery, void elements, implicit closing. Fundamentally different from XML. Could be a separate PR if there's interest.

Uses quick-xml to parse and re-indent XML bodies, similar to how
JSON responses are already formatted. Both buffered and streaming
paths are supported.

The xml.format and xml.indent format options are now accepted
(previously rejected as unsupported). Default indent is 4 spaces,
matching JSON.

Whitespace-only text nodes are stripped during formatting so that
already-indented XML gets re-indented cleanly without doubling up.
Mixed content whitespace is preserved.

Invalid XML falls back to syntax-highlight-only (no crash).

Closes ducaale#231
@o1x3
Copy link
Contributor Author

o1x3 commented Feb 20, 2026

@ducaale both comments addressed. Please let me know if you want these squashed into one commit

Copy link
Collaborator

@blyxxyz blyxxyz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very clean implementation, thank you!

The code looks good, haven't tested it yet.

@o1x3 o1x3 requested review from blyxxyz and ducaale February 21, 2026 17:03
- add xml format options to --format-options help text
- log xml format errors with log::debug!() before fallback
- handle Error::Io and Interrupted in format_xml
- remove trailing blank line in tests/cli.rs
@o1x3 o1x3 requested a review from blyxxyz February 22, 2026 17:36
Copy link
Collaborator

@blyxxyz blyxxyz left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for bearing with me. This time I tested it with a few different documents, particularly https://github.com/shlomif/perl-XML-LibXML/blob/master/example/test.xhtml. It works great!

The formatting isn't identical to HTTPie though:

Original XML
</p></dd><dt><strong><a name="item_toString">toString</a></strong></dt><dd><p><strong>toString</strong> is a deparsing function, so the DOM Tree can be translated into a string,
ready for output. The optional <strong>$format</strong> parameter sets the indenting of the output. This parameter is expected to
be an <em>integer</em> value, that specifies the number of linebreaks for each node. For more
information about the formatted output check the documentation of <em>xmlDocDumpFormatMemory</em> in <em>libxml2/tree.h</em> .

</p>
xh output
      </dd>
      <dt>
        <strong>
          <a name="item_toString">toString</a>
        </strong>
      </dt>
      <dd>
        <p>
          <strong>toString</strong> is a deparsing function, so the DOM Tree can be translated into a string,
ready for output. The optional <strong>$format</strong> parameter sets the indenting of the output. This parameter is expected to
be an <em>integer</em> value, that specifies the number of linebreaks for each node. For more
information about the formatted output check the documentation of <em>xmlDocDumpFormatMemory</em> in <em>libxml2/tree.h</em> .

</p>
HTTPie output
      </dd>
      <dt>
        <strong>
          <a name="item_toString">toString</a>
        </strong>
      </dt>
      <dd>
        <p>
          <strong>toString</strong>
           is a deparsing function, so the DOM Tree can be translated into a string,
ready for output. The optional
          <strong>$format</strong>
           parameter sets the indenting of the output. This parameter is expected to
be an
          <em>integer</em>
           value, that specifies the number of linebreaks for each node. For more
information about the formatted output check the documentation of
          <em>xmlDocDumpFormatMemory</em>
           in
          <em>libxml2/tree.h</em>
           .
        </p>
      </dd>

HTTPie changes the whitespace more. Each tag gets its own line even if it's nested between text nodes, and leading and trailing whitespace are removed.

It might be nice to do that too but it doesn't look like quick-xml has an easy way to do it? trim_text(true) produces results like this, which is IMO worse:

<p>As an equivalent of<strong>createElement</strong>, but it creates a<strong>Text Node</strong>bound to the DOM.</p>

So I'm fine with merging this.

@o1x3
Copy link
Contributor Author

o1x3 commented Feb 23, 2026

quick-xml gives you events one at a time (Text, Start, Text, End...). It doesn't give you "here's a mixed-content parent with all its children." You'd have to buffer children of each element, detect mixed content, then re-emit with custom splitting logic.

o1x3 and others added 3 commits February 23, 2026 12:02
Co-authored-by: Jan Verbeek <jan.verbeek@posteo.nl>
Co-authored-by: Jan Verbeek <jan.verbeek@posteo.nl>
@ducaale ducaale merged commit c27331a into ducaale:master Feb 24, 2026
@o1x3 o1x3 deleted the xml-pretty-print branch February 24, 2026 06:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: pretty print HTML / XML

3 participants