How to create XML with zero indentation?

According to the official Crystal documentation, you can generate XML in the following way.

require "xml"

string = XML.build_fragment(indent: "  ") do |xml|
  xml.element("person", id: 1) do
    xml.element("firstname") { xml.text "Jane" }
    xml.element("lastname") { xml.text "Doe" }
  end
end

puts string

Output:

<person id="1">
  <firstname>Jane</firstname>
  <lastname>Doe</lastname>
</person>

So, how do you set the indent to zero?

Expected output:

<person id="1">
<firstname>Jane</firstname>
<lastname>Doe</lastname>
</person>

You might think the following code will work.

require "xml"

string = XML.build_fragment(indent: "") do |xml| # also indent: 0
  xml.element("person", id: 1) do
    xml.element("firstname") { xml.text "Jane" }
    xml.element("lastname") { xml.text "Doe" }
  end
end

puts string

No, this does not work. It creates XML without line breaks. This is not what I want.

<person id="1"><firstname>Jane</firstname><lastname>Doe</lastname></person>

The indentation settings are implemented in builder.cr.

call is a macro for calling the native libxml function.

How to create XML with zero indentation?


Q Why do you need it?
A I want to mimic an XML file generated by a certain software. That software generates XML without indentation.

Seems like this is not possible with the current API. That doesn’t seem very reasonable though. I don’t get it why it does not allow a distinction between no indentation and no line breaks.

1 Like

Kind of a hack, but it seems to work if you use ​ U+200B ZERO WIDTH SPACE - Unicode Explorer as the indentation it works :sweat_smile:.

require "xml"

string = XML.build_fragment(indent: "\u200B") do |xml|
  xml.element("person", id: 1) do
    xml.element("firstname") { xml.text "Jane" }
    xml.element("lastname") { xml.text "Doe" }
  end
end

puts string
<person id="1">
<firstname>Jane</firstname>
<lastname>Doe</lastname>
</person>
1 Like

using indent: "\n" you get :see_no_evil:

<person id="1">

<firstname>Jane</firstname>

<lastname>Doe</lastname>
</person>
1 Like

That’s fun. :face_with_peeking_eye:
Also, ‘\b’ produces similar cheating outputs if don’t care the hidden character.
But there is something wrong with ‘\0’

Unhandled exception: String cannot contain null character (XML::Error)
1 Like

I suppose as a hacky workaround you can use some character that never shows up in the output (maybe from the Private Use Area) and then remove all occurance of that character from the result :see_no_evil:

EDIT: Or just monkey patch a working variant of XML::Builder#indent=

1 Like