kojix2
October 4, 2024, 12:39pm
1
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.
# Sets the indent string.
def indent=(str : String)
if str.empty?
call SetIndent, 0
else
call SetIndent, 1
call SetIndentString, string_to_unsafe(str)
end
end
# Sets the indent *level* (number of spaces).
def indent=(level : Int)
if level <= 0
call SetIndent, 0
else
call SetIndent, 1
call SetIndentString, " " * level
end
end
This file has been truncated. show original
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 .
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
<person id="1">
<firstname>Jane</firstname>
<lastname>Doe</lastname>
</person>
1 Like
That’s fun.
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
EDIT: Or just monkey patch a working variant of XML::Builder#indent=
1 Like