Skip to content

XML to Dictionary

Type: xml-to-dictionary  ·  Package: XML Activities v1.1.1  ·  Output: Dictionary<string,object>

Since: v1.1.1

Converts an XML document into a dictionary you navigate with expressions, just like a JSON read with Read JSON: invoice["ID"], invoice["InvoiceLine"][0]["ID"]. Use it when you need a lot of data from the same XML: instead of one XPath query per value, you convert once and read whatever you want. In the Spanish Designer it shows up as «XML a diccionario».

Reads the XML from xml (text) or from path (file; it honours the encoding the file declares, e.g. ISO-8859-1) and returns the content of the root element —or of the element chosen by xpath—. The element’s own name is not a key: the dictionary is directly its attributes, its text and its children. The rules:

In the XMLIn the dictionary
<cbc:ID>FE-1001</cbc:ID>"ID": "FE-1001" (text)
Element with attributes <cbc:PayableAmount currencyID="COP">1785000.00</…>"PayableAmount": { "@currencyID": "COP", "#text": "1785000.00" }
Element with attributes and no text <e x="2"/>"e": { "@x": "2", "#text": "" }
Element with childrenA dictionary with its children (and its @… attributes)
Repeated children (<cac:InvoiceLine> × 2)A list in document order
Empty element <cbc:Note/>""
Loose text between children (mixed content)"#text", with the pieces of text joined
CDATA sectionIts content, as text
xmlns:… declarationsThey don’t appear (they are not data)

The result is always a dictionary: if the element only has text, that text goes in "#text". Keys come out in the order they first appear in the document.

A complete example. This XML:

<Order number="P-77">
<Customer>Andina Trading</Customer>
<Line><Code>A-1</Code><Quantity>2</Quantity></Line>
<Line><Code>B-7</Code><Quantity>1</Quantity></Line>
<Total currency="USD">150.00</Total>
<Note/>
</Order>

gives this dictionary:

{
"@number": "P-77",
"Customer": "Andina Trading",
"Line": [
{ "Code": "A-1", "Quantity": "2" },
{ "Code": "B-7", "Quantity": "1" }
],
"Total": { "@currency": "USD", "#text": "150.00" },
"Note": ""
}
  • By default keys have no prefix (ID, InvoiceLine). With keepPrefixes = true they are written as in the document (cbc:ID, cac:InvoiceLine).
  • If two children from different namespaces would get the same key (e.g. a:ID and b:ID), all of the colliding ones keep their prefix: a:ID and b:ID. An element in the default namespace uses the defaultPrefix prefix (d:ID). If they still collide (the same prefix declared with two different URIs, or the document already uses d for another namespace), the key is {uri}name. The rule is the same for attributes (@type and @xsi:type).
  • The rule is deterministic: the same XML always gives the same keys, in document order.

An element that sometimes repeats (the lines of an invoice) is a list when there are several and a dictionary (or text) when there is one. To always go through it the same way, put it in forceList: ["InvoiceLine"]. The name works with or without prefix (InvoiceLine or cac:InvoiceLine) and the rule applies to that name at any level of the document.

All values stay as exact text: 1785000.00 stays "1785000.00", without losing zeros or precision, and codes with leading zeros (01) don’t change. If you turn on convertTypes (it also applies to attributes):

  • Integers → integer number (900000001); anything starting with 0 (except 0 itself) stays as text.
  • Decimals with a dot → exact decimal number (1785000.00 keeps its two decimals); if it has more digits than fit without rounding, it stays as text.
  • ISO dates → date: 2026-10-30 and 2026-09-30T10:15:00 as a date/time without time zone; 2026-09-30T10:15:00-05:00 or …Z as a date with time zone.
  • Everything else stays as text: +5, 1e5, true, bare times (10:15:00), texts longer than 64 characters and empty values.

None. You must provide one of xml or path (not both).

ParameterTypeDefaultDescription
xmlstring—XML text. Also the embedded XML that comes out of a CDATA (see the example). Use this or path.
pathstring—Path of an XML file. Use this or xml.
xpathstring—XPath of the element to convert, if you only need a fragment, e.g. "//cac:AccountingSupplierParty". The first match is used. Without it, the root element.
keepPrefixesboolfalseKeep namespace prefixes in the keys (cbc:ID instead of ID). When off, only names that would collide keep their prefix.
forceListList | string—Element names that are always a list, even when there is only one: a list (["InvoiceLine"]) or a comma-separated text ("InvoiceLine, Note").
convertTypesboolfalseConvert numbers and ISO dates into number and date values. When off, everything stays as exact text.
namespacesDictionary—Extra prefixes, or prefixes that override the document’s, for the xpath (prefix → URI). See Namespaces.
defaultPrefixstring"d"Prefix of the default namespace in the xpath and in colliding keys. Empty means "d".

Returns a Dictionary<string,object> whose values are text, dictionaries (Dictionary<string,object>) and lists (List); with convertTypes, also numbers and dates.

If xpath finds nothing, the output is null (it doesn’t fail) and the log shows a warning. Check it before reading keys.

MessageCause and fix
Indicate the XML text ('xml') or the file ('path').Neither was provided (or both were left empty).
Use 'xml' or 'path', not both.Both were provided. Keep only one.
File not found: …The path doesn’t exist.
The XPath must select an element.The xpath points to an attribute, a text or a computed value. Point to the element that contains it.
'keepPrefixes' must be true or false. / 'convertTypes' must be true or false.The parameter’s expression returned neither a boolean nor the text true/false.
XML parser / DTD / size / nesting / prefix messageSee Common package errors.

An element the XML doesn’t carry simply doesn’t appear as a key: if it is optional in your documents, check that it is there before reading it.

Example: embedded electronic invoice (fictitious data)

Section titled “Example: embedded electronic invoice (fictitious data)”

Colombia’s electronic invoice (DIAN, UBL 2.1) usually arrives as an AttachedDocument that carries the real invoice as text inside cac:Attachment/cac:ExternalReference/cbc:Description. Two steps: extract that text and convert it.

Read XML path = xmlPath → output: container
XML Query Text
xml = container
xpath = "//cac:Attachment/cac:ExternalReference/cbc:Description"
→ output: invoiceXml
XML to Dictionary
xml = invoiceXml
forceList = "InvoiceLine"
→ output: invoice
Set Variable name = taxId value = invoice["AccountingSupplierParty"]["Party"]["PartyTaxScheme"]["CompanyID"]["#text"]
Set Variable name = number value = invoice["ID"]
Set Variable name = issued value = invoice["IssueDate"]
Set Variable name = due value = invoice["DueDate"]
Set Variable name = total value = invoice["LegalMonetaryTotal"]["PayableAmount"]["#text"]
Set Variable name = cufe value = invoice["UUID"]["#text"]
For Each items = invoice["InvoiceLine"] itemVariable = line
└─ Log message = "Line " + str(line["ID"]) + ": " + str(line["LineExtensionAmount"]["#text"])

With the fictitious invoice used in the package tests, taxId = "900000001", number = "FE-1001", total = "1785000.00" and there are two lines.

Just a fragment, straight from the file:

XML to Dictionary path = invoicePath xpath = "//cac:AccountingSupplierParty" → output: issuer
Log message = "Issuer: " + str(issuer["Party"]["PartyTaxScheme"]["CompanyID"]["#text"])