The Azure Logic Apps Data Mapper has a visual designer in VS Code. You drag lines between schemas, drop in functions, and it generates an .lml file (YAML) that compiles to XSLT 3.0.
In theory, you never touch the YAML. In practice, the designer has reliability problems that make direct LML editing the better workflow.
Why skip the designer?
The designer doesn't always persist changes correctly:
-
Changes revert on reopen — save, close, reopen, and the
.lmlon disk has the old value -
String literals lose inner quotes —
xpath("'kg'")becomesxpath("kg"), changing a literal to an element reference -
Expressions get corrupted — conditions like
xpath("if (...) then 'Y' else 'N'")lose quote characters - Function arguments get rewritten — argument order or paths change silently
-
No undo across sessions — you need
git diffto catch silent corruption
These happen with normal operations, not edge cases.
What LML looks like
LML is YAML. Here's a complete map:
$version: 1
$input: XML
$output: XML
$sourceSchema: OrderSchema.xsd
$targetSchema: ShipmentOutput.xsd
$sourceNamespaces:
ns0: http://schemas.contoso.com/Order
$targetNamespaces:
xs: http://www.w3.org/2001/XMLSchema
Shipments:
Shipment:
ShipmentRef: prefixShipRef(/ns0:Order/ns0:OrderID)
ShipDate: reformatDate(/ns0:Order/ns0:OrderDate)
Recipient: /ns0:Order/ns0:Customer/ns0:CustomerName
Lines:
$for(/ns0:Order/ns0:Items/ns0:Item):
$if(xpath("ns0:Quantity >= 1")):
Line:
Product: ns0:ProductName
Quantity: ns0:Quantity
Header declares schemas and namespaces. Body maps target elements (left) to source expressions (right).
LML syntax cheat sheet
# Direct mapping
Recipient: /ns0:Order/ns0:Customer/ns0:CustomerName
# Attribute on parent element
$@shipId: /ns0:Order/ns0:OrderID
# Loop over repeating elements (paths inside are relative)
$for(/ns0:Order/ns0:Items/ns0:Item):
Line:
Product: ns0:ProductName
# Conditional
$if(xpath("ns0:Quantity >= 1")):
Line:
Product: ns0:ProductName
# Custom function (defined in Functions/*.xml)
ShipDate: reformatDate(/ns0:Order/ns0:OrderDate)
# Raw XPath escape hatch
Priority: xpath("upper-case(@priority)")
WeightUnit: xpath("'kg'") # inner quotes = string literal
Custom extension functions
Defined in Artifacts/DataMapper/Extensions/Functions/*.xml:
<customfunctions>
<function name="reformatDate" as="xs:string"
description="Reformats yyyy-MM-dd to dd/MM/yyyy.">
<param name="dateVal" as="xs:date"/>
<value-of select="format-date($dateVal, '[D01]/[M01]/[Y0001]')"/>
</function>
</customfunctions>
Gotchas
Zero-parameter functions crash the compiler. The SDK throws a NullReferenceException and silently skips the entire XML file. Workaround: add a dummy parameter.
Function arguments must be single-step element names. Multi-step paths cause compile errors — wrap them in xpath():
# INVALID
Address: formatAddress(ns0:Customer/ns0:Street, ns0:Customer/ns0:City)
# FIX
Address: formatAddress(xpath("ns0:Customer/ns0:Street"), xpath("ns0:Customer/ns0:City"))
The workflow: edit, compile, verify
1. Edit the YAML
Open the .lml file in any text editor and make your change.
2. Compile to XSLT
Install lml-compile, a dotnet global tool that wraps the Logic Apps SDK:
dotnet tool install -g lml-compile
Compile:
lml-compile Artifacts/MapDefinitions/MyMap-lml.lml Artifacts/Maps/MyMap-lml.xslt
# OK: MyMap-lml.xslt
3. Verify
Run dotnet test or press F5 with the XSLT Debugger extension.
Auto-compile on save
Install the Run on Save extension and add this to your workspace settings (for multi-root workspaces, add it in the .code-workspace file — Run on Save doesn't read folder-level settings):
"emeraldwalk.runonsave": {
"commands": [
{
"match": "\\.lml$",
"cmd": "lml-compile ${file} ${fileDirname}/../Maps/${fileBasenameNoExt}.xslt"
}
]
}
Note: Run on Save uses its own variables — not VS Code task variables.
${fileBasenameNoExt}and${fileDirname}work;${fileBasenameNoExtension}and${workspaceFolder}do not. Reload the window after adding the config.
Now the cycle is just: edit .lml → save → F5 or dotnet test. The compile happens automatically.
What the compiler catches
- Malformed YAML / missing
$versionheader - Missing schemas in
Artifacts/Schemas/ - Unrecognized function calls
- Invalid XPath syntax
Errors surface on save instead of at deploy time.
Naming convention
Name hand-authored LML files with a -lml suffix:
Artifacts/MapDefinitions/OrderToShipment-lml.lml → Artifacts/Maps/OrderToShipment-lml.xslt
This avoids colliding with hand-authored XSLT files and makes it clear which files are compiled output.
Summary
Use the designer to scaffold the initial map. Then switch to direct LML editing for all subsequent changes.
Edit .lml → save → auto-compile → verify
No designer round-trip. No silent rewrites. No surprises.
The XSLT Debugger extension is on the VS Code Marketplace for macOS and Windows.
Top comments (0)