How to Dynamically Convert XML Elements into Key-Value Tags Using XSLT and Python
When integrating disparate systems or transforming data into normalized schemas, you often need to pivot dynamic XML tags into a standardized key-value representation. Instead of having unique tags like <userid>, <name>, or <rating>, you may need a generic structure where tag names become values inside an <elementname> field.
In this guide, we will walk through the standard, highly efficient methods to dynamically transform XML elements into key-value pairs using both XSLT (the industry standard for XML transformations) and Python.
The Problem Definition
Suppose you have the following incoming XML payload:
<fields>
<userid>1</userid>
<name>Tom</name>
<rating>A</rating>
</fields>You need to transform it dynamically into a generic key-value format without hardcoding tag names:
<fields>
<field>
<elementname>userid</elementname>
<elementvalue>1</elementvalue>
</field>
<field>
<elementname>name</elementname>
<elementvalue>Tom</elementvalue>
</field>
<field>
<elementname>rating</elementname>
<elementvalue>A</elementvalue>
</field>
</fields>Solution 1: The Canonical XSLT Approach
The cleanest and most portable way to achieve this is by using XSLT (Extensible Stylesheet Language Transformations). XSLT allows you to query node names dynamically using the built-in local-name() or name() functions.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output method="xml" indent="yes" omit-xml-declaration="no"/>
<!-- Match root element -->
<xsl:template match="/fields">
<fields>
<!-- Apply templates to all child elements dynamically -->
<xsl:apply-templates select="*"/>
</fields>
</xsl:template>
<!-- Match any child element -->
<xsl:template match="fields/*">
<field>
<elementname><xsl:value-of select="local-name()"/></elementname>
<elementvalue><xsl:value-of select="."/></elementvalue>
</field>
</xsl:template>
</xsl:stylesheet>How It Works:
select="*": Selects any direct child element under<fields>regardless of its tag name.local-name(): Dynamically extracts the tag name of the current node, ignoring XML namespaces..: Selects the text content inside the current node.
Solution 2: Programmatic Transformation Using Python
If you are processing XML in backend pipelines with Python, you can achieve this transformation using Python's standard xml.etree.ElementTree module without requiring external libraries.
import xml.etree.ElementTree as ET
xml_input = """<fields>
<userid>1</userid>
<name>Tom</name>
<rating>A</rating>
</fields>"""
# Parse source XML
root = ET.fromstring(xml_input)
# Create new root element
new_root = ET.Element("fields")
# Iterate dynamically through child elements
for child in root:
field = ET.SubElement(new_root, "field")
name_elem = ET.SubElement(field, "elementname")
name_elem.text = child.tag
val_elem = ET.SubElement(field, "elementvalue")
val_elem.text = child.text
# Convert back to XML string with indentation
ET.indent(new_root, space=" ")
output_xml = ET.tostring(new_root, encoding="unicode")
print(output_xml)Key Considerations and Best Practices
- Namespaces: If your XML tags have namespaces (e.g.,
<ns:userid>),local-name()will strip the prefix and returnuserid. If you need the prefix intact, usename()in XSLT. - Attributes: If the original elements contain attributes, decide whether they should be migrated to attributes on
<field>or included as secondary elements inside<elementvalue>. - Performance: For high-throughput enterprise pipelines (such as MuleSoft, BizTalk, or Camel), prefer the compiled XSLT approach for maximum performance and separation of concerns.