xmerl_sax_parser(3)

πŸ“¦
version 29.0.6 Β· 2 versions
, in nixos 26.05, nixpkgs unstable, ubuntu stonking

✨ NAME ✨

xmerl_sax_parser β€” it's the XML SAX parser API, bestie πŸ’–


🌸 DESCRIPTION 🌸

okay so picture this: it's a SAX parser for XML, and what it does is it sends the events through a callback interface πŸŽ€ like, the parser walks the XML and every time something happens it just yells it at your callbacks. main character energy fr fr.

SAX stands for Simple API for XML β€” yes the acronym really spells SAX and not something cute, we work with what we've got πŸ’…

ngl the lore is kinda iconic: SAX was originally a Java-only API πŸ‘‘ like, exclusively Java, nobody else invited. But it was the first widely adopted API for XML in Java, and from there it became a de facto standard β€” the way that a thing just becomes the standard by everyone using it?? obsessed πŸ¦‹

and because it got so big, there are now versions of it for several programming language environments other than Java 🌈 so it broke out of the Java-only era and went multi-language. we love an inclusive glow-up ✨

πŸ’€ not me explaining SAX like it's tea but genuinely that's the whole vibe check here: events β†’ callbacks, Java origin story, de facto standard, now everywhere.

more coming, don't go anywhere 🫢

πŸ’– DATA TYPES πŸ’–

Okay bestie, this is the part where we meet all the little building blocks 🧱✨ Every single one of them, no skipping, we're being thorough girlies today.


continuation_fun()

continuation_fun() ::
          fun((continuation_state()) ->
                  {NewBytes :: binary() | list(),
                   continuation_state()}).

This is the callback function/0 that gets pinged whenever the parser runs out of input data. 😭

If the function literally cannot get its hands on more input, it hands back an empty list or an empty binary (which one depends on what you started the input as in stream/2). Any other kind of error gets handled through exceptions. If the continuation function hits a fatal error, use throw/1 to fling this tuple:

{Tag = atom(), Reason = string()}

<Tag> is an atom that IDs the functional entity sending the exception, and <Reason> is a string describing what actually went wrong. Tag + Reason, the iconic duo πŸ’…


continuation_state()

continuation_state() :: term().

event()

event() ::
          startDocument | endDocument |
          {startPrefixMapping, Prefix :: string(), Uri :: string()} |
          {endPrefixMapping, Prefix :: string()} |
          {startElement,
           Uri :: string(),
           LocalName :: string(),
           QualifiedName :: string(),
           Attributes :: string()} |
          {endElement,
           Uri :: string(),
           LocalName :: string(),
           QualifiedName :: string()} |
          {characters, string()} |
          {ignorableWhitespace, string()} |
          {processingInstruction, Target :: string(), Data :: string()}
|
          {comment, string()} |
          startCDATA | endCDATA |
          {startDTD,
           Name :: string(),
           PublicId :: string(),
           SystemId :: string()} |
          endDTD |
          {startEntity, SysId :: string()} |
          {endEntity, SysId :: string()} |
          {elementDecl, Name :: string(), Model :: string()} |
          {attributeDecl,
           ElementName :: string(),
           AttributeName :: string(),
           Type :: string(),
           Mode :: string(),
           Value :: string()} |
          {internalEntityDecl, Name :: string(), Value :: string()} |
          {externalEntityDecl,
           Name :: string(),
           PublicId :: string(),
           SystemId :: string()} |
          {unparsedEntityDecl,
           Name :: string(),
           PublicId :: string(),
           SystemId :: string(),
           Ndata :: string()} |
          {notationDecl,
           Name :: string(),
           PublicId :: string(),
           SystemId :: string()}.

These are the SAX events that get slid over to you through the event callback. Here's the whole roster, one by one πŸ‘‘πŸ‘‡

  • startDocument β€” You're getting notified that the document is beginning. The SAX parser sends this event exactly once, before any other event callback fires. One and done bestie πŸ’–
  • endDocument β€” Notification that the document is done. The SAX parser sends this exactly once, and it's the last event of the whole parse. Closing time πŸŒ™
  • {startPrefixMapping, Prefix, Uri} β€” Starts the scope of a prefix-to-URI Namespace mapping. Heads up though β€” start/endPrefixMapping events are not guaranteed to be properly nested relative to each other. All startPrefixMapping events show up immediately before the matching startElement event, and all endPrefixMapping events show up immediately after the matching endElement event, but past that their order is not guaranteed at all. Also, there will be no start/endPrefixMapping events for the "xml" prefix, because it's predeclared and immutable. It's giving untouchable πŸ’…
  • {endPrefixMapping, Prefix} β€” Ends the scope of a prefix-to-URI mapping.
  • {startElement, Uri, LocalName, QualifiedName, Attributes} β€” Notification that an element is starting. The Parser sends this at the beginning of every single element in the XML document, and there will be a matching endElement for every startElement (even when the element is empty!). All of the element's content gets reported in order, before the corresponding endElement. Fully accounted for, no cap.
  • {endElement, Uri, LocalName, QualifiedName} β€” Notification that an element is ending. The SAX parser sends this at the end of every element in the XML document, and every endElement has a matching startElement (again, even when the element is empty). Symmetry, slay ✨
  • {characters, string()} β€” Notification of character data.
  • {ignorableWhitespace, string()} β€” Notification of ignorable whitespace sitting in element content.
  • {processingInstruction, Target, Data} β€” Notification of a processing instruction. The Parser sends this once for every processing instruction it finds β€” and note that processing instructions can show up before or after the main document element. They roam free πŸ¦‹
  • {comment, string()} β€” Reports an XML comment anywhere in the document, both inside and outside of the document element.
  • startCDATA β€” Reports the start of a CDATA section. The actual contents of the CDATA section get reported through the regular characters event.
  • endCDATA β€” Reports the end of a CDATA section.
  • {startDTD, Name, PublicId, SystemId} β€” Reports the start of DTD declarations, i.e. the start of the DOCTYPE declaration. If the document has no DOCTYPE declaration, this event simply won't be sent. πŸ™ˆ
  • endDTD β€” Reports the end of DTD declarations, i.e. the end of the DOCTYPE declaration.
  • {startEntity, SysId} β€” Reports the beginning of some internal and external XML entities.
  • {endEntity, SysId} β€” Reports the end of an entity.
  • {elementDecl, Name, Model} β€” Reports an element type declaration. The content model is either the string "EMPTY", the string "ANY", or a parenthesised group optionally followed by an occurrence indicator. The model gets normalized so all parameter entities are fully resolved and all whitespace is removed, and it will include the enclosing parentheses. Any further normalization (like removing redundant parentheses or simplifying occurrence indicators) is left to the discretion of the parser.
  • {attributeDecl, ElementName, AttributeName, Type, Mode, Value} β€” Reports an attribute type declaration.
  • {internalEntityDecl, Name, Value} β€” Reports an internal entity declaration.
  • {externalEntityDecl, Name, PublicId, SystemId} β€” Reports a parsed external entity declaration.
  • {unparsedEntityDecl, Name, PublicId, SystemId, Ndata} β€” Notification of an unparsed entity declaration event.
  • {notationDecl, Name, PublicId, SystemId} β€” Notification of a notation declaration event.

event_fun()

event_fun() ::
          fun((event(), event_location(), event_state()) ->
                  event_state()).

This is the callback function/0 that fires for every. single. event the parser sends. 😀 Obsessed with the dedication tbh.

Error handling here goes through exceptions. Use throw/1 to fling this tuple:

{Tag = atom(), Reason = string()}

...if the application hits a fatal error. <Tag> is an atom identifying the functional entity that sends the exception, and <Reason> is a string describing the problem. πŸ’€


event_location()

event_location() ::
          {CurrentLocation :: string(),
           Entityname :: string(),
           LineNo :: integer()}.

event_state()

event_state() :: term().

latin1_binary()

latin1_binary() :: unicode:latin1_binary().

A binary with characters encoded in iso-latin-1. πŸ“


options()

options() ::
          [{continuation_fun, continuation_fun()} |
           {continuation_state, continuation_state()} |
           {event_fun, event_fun()} |
           {event_state, event_state()} |
           {file_type, normal | dtd} |
           {encoding,
            utf | {utf16, big} | {utf16, little} | latin1 | list} |
           skip_external_dtd | disallow_entities |
           {entity_recurse_limit, non_neg_integer()} |
           {external_entities, all | file | none} |
           {fail_undeclared_ref, boolean()} |
           {discard_ws_before_xml_document, boolean()}].

These are the options used to customize how the parser behaves. The full list of possible options is right here bestie, don't scroll past πŸ‘‡

  • {continuation_fun, ContinuationFun} β€” <ContinuationFun> is the callback function that decides what to do when the parser runs into EOF before the document is complete.
  • {continuation_state, ContinuationState} β€” State that's reachable inside the continuation callback function.
  • {event_fun, EventFun} β€” <EventFun> is the callback function for parser events.
  • {event_state, EventState} β€” State that's reachable inside the event callback function.
  • {file_type, FileType} β€” A flag that tells the parser whether it's parsing a DTD or a normal XML file. Default is normal.
  • {encoding, Encoding} β€” Sets the default character set to use. Default is utf, meaning UTF-8. This character set is used only if it isn't explicitly given by the XML document itself.
  • skip_external_dtd β€” Skips the external DTD while parsing. This option is the same as {external_entities, none} and {fail_undeclared_ref, false}, except it applies just to the DTD.
  • disallow_entities β€” Implies that parsing fails if an ENTITY declaration is found. Big yikes energy, do not pass go.
  • {entity_recurse_limit, N} β€” Sets how many levels of recursion are allowed for entities. Default is 3 levels.
  • {external_entities, Allowed} β€” Sets which types of external entities are allowed; if it's not allowed, it just gets skipped. Default is none.
  • {fail_undeclared_ref, Boolean} β€” Decides how the parser behaves when an undeclared reference is found. Can be handy if you've turned off external entities so that an external DTD isn't parsed. Default is true.
  • {discard_ws_before_xml_document, Boolean} β€” Discards whitespace before the xml tag instead of returning a fatal error. Default is false. 🫢

unicode_binary()

unicode_binary() :: binary().

A binary with characters encoded in UTF-8 or UTF-16. 🌈


unicode_char()

unicode_char() :: char().

An integer representing a valid unicode codepoint. ⭐

✨ FUNCTIONS ✨

okay bestie, these are the two functions you'll actually call β€” and yes, they're both iconic πŸ’…

file(Name, Options)

       file(Name, Options) -> {ok, EventState, Rest} | ErrorOrUserReturn
                     when
                         Name :: file:filename(),
                         Options :: options(),
                         EventState :: event_state(),
                         Rest :: unicode_binary() | latin1_binary(),
                         ErrorOrUserReturn ::
                             {Tag, Location, Reason, EndTags, EventState} |
                             {error, {Name, Reason}},
                         Tag :: fatal_error | atom(),
                         Location :: event_location(),
                         Reason :: term(),
                         EndTags :: term().

This one parses a file that contains an XML document, bestie. It uses a default continuation function to read the file in blocks β€” so like, it's reading your file in cute little chunks, no cap. 🫢

The return vibe is either {ok, EventState, Rest} or ErrorOrUserReturn, and all the type details are right there in the when clause above. Periodt. πŸ’–


stream(Xml, Options)

       stream(Xml, Options) -> {ok, EventState, Rest} | ErrorOrUserReturn
                       when
                           Xml ::
                               unicode_binary() |
                               latin1_binary() |
                               [unicode_char],
                           Options :: options(),
                           EventState :: event_state(),
                           Rest :: unicode_binary() | latin1_binary(),
                           ErrorOrUserReturn ::
                               {Tag, Location, Reason, EndTags, EventState},
                           Tag :: fatal_error | atom(),
                           Location :: event_location(),
                           Reason :: term(),
                           EndTags :: term().

This one parses a stream containing an XML document. That's it, that's the function. Slay. ✨

no date on this footer, but the source/version energy is immaculate anyway.

shoutout to Ericsson AB, xmerl 2.2, and the whole xmerl_sax_parser(3) manual page for the footer vibes. bye bestie, go parse something gorgeous πŸ¦‹πŸ’–

πŸ“¦ where this page comes from