module

Mica::XML::Fields

Reads an XML document into the same field hash a JSON object body produces, so one schema can validate either format. Web frameworks use it as a request-body parser; it is opt-in (require "mica/xml/fields") because it pulls in stdlib JSON.

The root element stands for the JSON object itself, so its name is not a key. Each child element becomes a field named by its local name:

  • an element with only text becomes that text, exactly (<name>Ada</name> gives "name" => "Ada"; an empty element gives "");
  • an element with attributes or child elements becomes a nested object; its own text is the "#text" key, kept exactly when it has no child elements, and with surrounding white space removed (and left out when blank) when the text is mixed in among child elements;
  • a name that repeats among siblings becomes an array, in document order;
  • attributes become "@name" keys of their element's object.

Values stay strings; the schema converts them, as it does for form data.

A request body is untrusted, so read streams it once through Mica::XML::Reader (no DOM is built) and raises Fields::RejectedDocumentError at the first sign of what a field hash cannot carry faithfully or safely: a document type declaration (DTD), refused at the <!DOCTYPE before any declaration is read; sibling elements or attributes that share a local name but not a namespace; or nesting deeper than MAX_DEPTH. Every event and every copied byte is charged to the parse's work meter, so the whole read stays linear in the body's size.

require "mica/xml/fields"

Mica::XML::Fields.read(%(<user id="7"><name>Ada</name><role>admin</role><role>dev</role></user>))
# => {"@id" => "7", "name" => "Ada", "role" => ["admin", "dev"]}

Constants

MAX_BODY_BYTES = (1_i64 * 1024) * 1024

The largest body read accepts with its default options.

MAX_DEPTH = 64

The deepest element nesting read accepts, whatever options it is given. The root element is depth 1.

REQUEST_BODY_OPTIONS = ParserOptions.new(allow_document_type: false, max_element_depth: MAX_DEPTH, max_input_bytes: MAX_BODY_BYTES)

Parser options for request bodies: the secure defaults, with no DTD, nesting capped at MAX_DEPTH and the body capped at MAX_BODY_BYTES.

Class methods

read(source : String | Bytes | IO, options : ParserOptions = REQUEST_BODY_OPTIONS) : Hash(String, JSON::Any)

Parses source and returns its fields. Raises the errors of Mica::XML::Reader (Mica::ParseError, Mica::LimitExceededError, Mica::EncodingError) when the body is not well-formed or trips a limit, the work budget or the deadline, and RejectedDocumentError as described on Fields. Custom options must keep what the mapping relies on: allow_document_type: false, namespace_aware: true and a max_input_bytes cap; ArgumentError otherwise.

Source

Nested types