diff --git a/en/docs/develop/transform/xml.md b/en/docs/develop/transform/xml.md index 9ffcd35237d..33c0b097d6e 100644 --- a/en/docs/develop/transform/xml.md +++ b/en/docs/develop/transform/xml.md @@ -2,6 +2,7 @@ sidebar_position: 3 title: XML Processing description: Parse, construct, transform, and validate XML data. +keywords: [wso2 integrator, xml, parse, transform, xslt, xsd validation] --- import Tabs from '@theme/Tabs'; @@ -13,22 +14,47 @@ XML is a flexible, text-based format used to store, transport, and structure dat WSO2 Integrator provides built-in support for XML processing, making it easy to work with XML data in integration scenarios. You can create, read, query, modify, validate, and transform XML content without relying on external libraries. This native XML support simplifies integration development and helps efficiently process XML payloads exchanged between applications, services, and enterprise systems. -## XML literals and construction +## Construct -Create XML values directly in WSO2 Integrator using backtick templates. The `xml` type supports XML elements, text nodes, comments, and processing instructions, making it easy to construct structured XML payloads within integrations. +Use backtick templates and namespace declarations to build XML payloads from static values, dynamic expressions, or existing data structures. + +### Building XML payloads + +The `xml` type in WSO2 Integrator can hold four kinds of XML nodes: elements, text nodes, comments, and processing instructions. Use XML template literals (backtick syntax: `` xml `...` ``) to construct any of these and store the value in a variable declaration node in your integration flow. -1. **Add a Variable**: In the flow designer, click **+** and select **Declare Variable**. Set the variable type to `xml` and provide an XML backtick template as the expression (for example: **xml ``**). +1. **Add a Variable**: In the visual designer, click **+** and select **Declare Variable**. Set the **Name** to the variable name (for example, `xmlPayload`) and the **Type** to `xml`. + +2. **Enter an XML template**: In the **Expression** field, write an XML template using backtick syntax. The sample below covers all four node kinds and is ready to copy as a starting point: + + ``` + xml ` + + Acme Corp + 250.00 + + ` + ``` + + ![Declare Variable side panel showing xmlPayload variable with type xml and the order XML template in the expression field](/img/develop/transform/xml/xml-payload.png) -2. **Use embedded expressions**: Insert dynamic values into XML templates using `${variableName}` syntax. Each XML variable is represented as a separate **Declare Variable** step in the flow, allowing you to visually manage XML construction. +3. **Insert dynamic values**: To embed a runtime value inside the XML template, use the `${expression}` syntax directly within the backtick template. The expression can be any valid Ballerina expression: - ![Flow designer showing Declare Variable for XML literal construction including dynamic expressions](/img/develop/transform/xml/xml-literals-flow.png) + | Syntax | Example | Inserts | + |---|---|---| + | `${variableName}` | `${customerId}` | Value of a variable | + | `${record.field}` | `${order.total}` | A record field value | + | `${functionCall()}` | `${generateId()}` | Return value of a function | -3. **Configure the expression**: Select a variable node to view and edit the XML template expression from the side panel. + Once you type an expression, the visual designer renders it as a blue chip inside the expression editor. For example, `{x} customerId` appears as a chip, so expressions are visually distinct from the surrounding XML text. - ![Side panel showing the dynamic XML variable with embedded expression](/img/develop/transform/xml/xml-literals-dynamic-panel.png) + ![Declare Variable panel showing xmlPayload with a dynamic expression rendered as a blue chip in the expression field](/img/develop/transform/xml/dynamic-xml.png) + +:::info +Text nodes, comments, and processing instructions are rarely constructed manually in integration flows. They appear more often when reading and transforming incoming XML payloads. +::: @@ -37,96 +63,148 @@ Create XML values directly in WSO2 Integrator using backtick templates. The `xml import ballerina/io; public function main() { - // XML element - xml greeting = xml `Hello, World!`; - - // Nested elements - xml orders = xml ` + // The xml type holds elements, text nodes, comments, and processing instructions. + // Use xml `...` templates to construct XML values and store them in variables. + xml payload = xml ` + Acme Corp - - - - + 250.00 + `; - // XML with embedded expressions - string name = "Globex Inc"; - int quantity = 10; - xml dynamic = xml ` - ${name} - ${quantity} - `; + // Embed runtime values using ${} interpolation + string customerId = "CUST-42"; + decimal amount = 375.50; + xml dynamic = xml ` + ${customerId} + ${amount} + `; + io:println(payload); io:println(dynamic); } ``` +:::info +Text nodes, comments, and processing instructions are rarely constructed manually in integration flows. They appear more often when reading and transforming incoming XML payloads. +::: + -### XML text and comments +### Namespaces + +A namespace declaration binds a URI to a short prefix, which you then use in XML element names and navigation expressions. The syntax is: + +``` +xmlns "namespace-URI" as prefix; +``` + +The scope of a declaration depends on where you place it: -Create XML text nodes, comments, and processing instructions directly using XML backtick templates. These XML node types can be stored in variables and used when building or transforming XML payloads. +- **Module level** (below the `import` statements in the `.bal` file): available to all functions in the file. +- **Function level** (inside a function body): scoped to that function only. -1. **Add Variable**: In the flow designer, add separate **Declare Variable** with the type set to `xml` for each XML node type such as text, comment, or processing instruction. +:::info +`xmlns` declarations cannot be added through the Visual Designer. Open the `.bal` file directly and write the binding below the `import` statements for module-level scope, or inside the function body for function-level scope. +::: + +1. **Declare the namespace**: Write the `xmlns` binding in the appropriate scope. For example, to make the `ord` prefix available across the whole file, place it below the `import` statements: -2. **Define XML node values**: Provide the required XML backtick template expression for the node type you want to create. + ```ballerina + import ballerina/io; -3. **View the flow representation**: Each XML node appears as an individual **Declare Variable** step in the integration flow. + xmlns "http://example.com/orders" as ord; + ``` -![Flow designer showing Declare Variable for XML text, comment, and processing instruction nodes](/img/develop/transform/xml/xml-text-comments-flow.png) + To limit the prefix to a single function, place it inside the function body instead. - +2. **Use the prefix in XML templates**: Once declared, use the prefix in backtick templates to construct namespaced XML. Add a **Declare Variable** step, set the type to `xml`, and enter the namespaced XML template as the expression. + + ![Declare Variable side panel showing nsOrder variable with type xml and a namespaced XML template using the ord prefix in the expression field](/img/develop/transform/xml/xml-ns.png) + ```ballerina import ballerina/io; -public function main() { - // Text node - xml text = xml `Hello, World!`; +// Module-level declaration: placed below imports, available to all functions in this file +xmlns "http://example.com/orders" as ord; - // Comment - xml comment = xml ``; +public function main() { + // Function-level declaration: scoped to this function only + xmlns "http://example.com/common" as cmn; - // Processing instruction - xml pi = xml ``; + xml nsOrder = xml ` + Acme Corp + 1500.00 + `; - io:println(text); - io:println(comment); - io:println(pi); + io:println(nsOrder); } ``` - -## Navigating XML +## Read and query + +Use XML navigation expressions and iteration to extract and process data from XML payloads. -Access child elements, attributes, and text content using XML navigation expressions in WSO2 Integrator. XML navigation makes it easy to query and extract specific parts of XML payloads during integration flows. +### Navigating XML + +WSO2 Integrator provides built-in XML navigation expressions that let you traverse an XML structure without writing loops. These expressions work on the `xml` type directly and return an `xml` value containing the matched nodes. -1. **Define the XML input**: In the flow designer, click **+** and select **Declare Variable**. Set the type to `xml` and enter the XML literal as the expression. Name the variable `catalog`. This variable is then referenced in all subsequent navigation steps. +**Declare the XML payload** + +Click **+** and select **Declare Variable**. Set the **Name** to `items` and the **Type** to `xml`. Enter the sample XML below as the **Expression**. All navigation steps below reference this variable. -2. **Navigate child elements**: Add a **Declare Variable** step and use expressions such as `catalog/` to select child elements by name, or `catalog/*` to retrieve all child elements. +``` +xml ` + + + A Study in Scarlet + Arthur Conan Doyle + + Daily Plannerday365 + + The Sign of Four + Arthur Conan Doyle + + markerblue +` +``` + +**Navigate the payload** -3. **Access text content**: Use the `.data()` function in a variable expression (for example, `(firstProduct/).data()`) to extract the text value of an XML element. +For each expression, add a **Declare Variable** node, set the **Type** to `xml`, and enter the expression in the **Expression** field. -4. **Access attributes**: Use `.getAttributes()["attributeName"]` to retrieve attribute values from an XML element. +| Expression | Variable name | Returns | +|---|---|---| +| `items.` | `root` | The `` element itself | +| `items/*` | `allChildren` | All children, including text nodes and comments | +| `items/` | `books` | Both `` direct children | +| `items/` | `plannerOrPen` | The `` and `` children | +| `items/<*>` | `elementChildren` | All element-type children, excluding text and comments | +| `items/**/` | `allNames` | All `` elements at any depth | +| `items/[0]` | `firstBook` | The first `` child (zero-based index) | -5. **Filter descendants**: Use descendant navigation expressions such as `catalog/**/` to find matching elements at any level of the XML hierarchy. +To read the text value of a matched element, chain `.data()` on the expression and set the **Type** to `string`. For example, name the variable `title` and enter `(items/[0]/).data()` to get `A Study in Scarlet`. - ![Flow designer showing Declare Variable for XML navigation including child access, text content, attributes, and descendant filtering](/img/develop/transform/xml/xml-navigating-flow.png) +**Navigate namespaced elements** -6. **View and edit expressions**: Select a variable node to view or modify the XML navigation expression from the side panel. +The same navigation expressions work with namespace prefixes. Add the `xmlns` binding to your `.bal` file first (see [Namespaces](#namespaces)), then add a **Declare Variable** node using the prefixed expression in the **Expression** field. - ![Side panel showing the products variable with catalog child access expression](/img/develop/transform/xml/xml-navigating-child-panel.png) +| Expression | Variable name | Returns | +|---|---|---| +| `nsOrder/` | `customer` | The `` direct child | +| `nsOrder/**/` | `orderItems` | All `` elements at any depth | @@ -136,6 +214,179 @@ Access child elements, attributes, and text content using XML navigation express import ballerina/io; public function main() { + xml items = xml ` + + + A Study in Scarlet + Arthur Conan Doyle + + Daily Plannerday365 + + The Sign of Four + Arthur Conan Doyle + + markerblue + `; + + // x.: every element named `items` within `items` itself + xml root = items.; + io:println(root); + // Output: ............ + + // x/*: all children (elements, text nodes, and comments) + xml allChildren = items/*; + io:println(allChildren); + // Output: ............ + + // x/: every element named `book` in the children of each element in `items` + xml books = items/; + io:println(books); + // Output: A Study in ScarletArthur Conan Doyle + // The Sign of FourArthur Conan Doyle + + // x/: every element named `planner` or `pen` in the children + xml plannerOrPen = items/; + io:println(plannerOrPen); + // Output: Daily Plannerday365 + // markerblue + + // x/<*>: every element child (excludes text nodes and comments) + xml elementChildren = items/<*>; + io:println(elementChildren); + // Output: ............ + + // x/**/: every element named `name` anywhere in the descendants + xml allNames = items/**/; + io:println(allNames); + // Output: A Study in ScarletArthur Conan Doyle + // The Sign of FourArthur Conan Doyle + + // x/[n]: the element at index n in the matched sequence (zero-based) + xml firstBook = items/[0]; + io:println(firstBook); + // Output: A Study in ScarletArthur Conan Doyle + + // Chain .data() to extract the text content of a matched element + string title = (items/[0]/).data(); + io:println(title); + // Output: A Study in Scarlet +} +``` + + + +#### Accessing attributes + +WSO2 Integrator provides two ways to read attribute values from an XML element: attribute expressions for concise single-attribute access, and `getAttributes()` when you need the full attribute map. + +**Attribute expressions** + +Use dot notation directly on an `xml` value to read an attribute by name: + +- `check x.attrName` reads a required attribute. It returns an `error` if the attribute does not exist or if `x` is not a singleton element. +- `check x?.attrName` reads an optional attribute. It returns `()` if the attribute does not exist. + +**`getAttributes()` function** + +Call `(x).getAttributes()` to get a `map` of all attributes on the element, including namespace attributes. Index the map with `["attrName"]` to retrieve a single value as `string?`. + + + + +Start by declaring the XML payload. Add a **Declare Variable** node, name it `product`, set the type to `xml`, and enter the sample below as the **Expression**: + +``` +xml `Hello` +``` + +For each attribute read, add another **Declare Variable** node and enter the expression in the **Expression** field. + +**Required attribute: `check x.attrName`** + +Name the variable `id`, set the type to `string`, and enter `check product.id`. + +Output: `greeting` + +If the attribute does not exist, this expression returns an error. Use this form when the attribute is guaranteed to be present. + +**Optional attribute: `check x?.attrName`** + +Name the variable `lang`, set the type to `string?`, and enter `check product?.lang`. + +Output: `en` + +For an attribute that may be absent, name the variable `sku`, set the type to `string?`, and enter `check product?.sku`. + +Output: `()`. The attribute is not on the element, so the result is nil. + +**All attributes: `getAttributes()`** + +Name the variable `attrs`, set the type to `map`, and enter `(product).getAttributes()`. + +Output: `{"id":"greeting","lang":"en"}` + +To read a single value from the map, add another **Declare Variable** node, name it `attrById`, set the type to `string?`, and enter `attrs["id"]`. + +Output: `greeting` + + + + +```ballerina +import ballerina/io; + +public function main() returns error? { + xml product = xml `Hello`; + + // Attribute expressions: concise single-attribute access + + // x.attrName (required): errors if the attribute does not exist + string id = check product.id; + io:println(id); // Output: greeting + + // x?.attrName (optional): returns () if the attribute does not exist + string? lang = check product?.lang; + string? missing = check product?.sku; + io:println(lang); // Output: en + io:println(missing); // Output: () + + // getAttributes(): returns a map of all attributes at once + map attrs = (product).getAttributes(); + io:println(attrs); // Output: {"id":"greeting","lang":"en"} + io:println(attrs["id"]); // Output: greeting +} +``` + + + + +#### Filtering elements by attribute value + +XML navigation expressions in WSO2 Integrator do not support attribute-based predicate filtering directly. To filter elements by attribute value, use a query expression over the navigation result. To select by position, use the `[n]` index syntax on the navigation expression. + + + + +1. **Filter by attribute value**: Add a **Declare Variable** step and enter a query expression that iterates over a navigation result and filters using the optional attribute expression. For example, to select only `` elements where the `category` attribute equals `"electronics"`: + + ``` + from xml p in catalog/ + let string? category = check p?.category + where category == "electronics" + select p + ``` + +2. **Select by position**: To get a single element at a known index, use the `[n]` index syntax directly in the expression field. For example, `catalog/[0]` returns the first `` child. + +3. **Extract text from the result**: Chain `.data()` on a positional expression to read the text content. For example, `(catalog/[0]/).data()` returns the name of the first product. + + + + +```ballerina +import ballerina/io; + +public function main() returns error? { xml catalog = xml ` Widget @@ -145,48 +396,150 @@ public function main() { Gadget 49.99 + + SmartHub + 89.99 + `; - // Get child elements by name - xml products = catalog/; + // Filter by attribute value using the optional attribute expression + xml electronics = from xml p in catalog/ + let string? category = check p?.category + where category == "electronics" + select p; + io:println(electronics); + + // Position-based access: first child (zero-based index) + xml firstProduct = catalog/[0]; + io:println(firstProduct); + + // Extract text from a positional result + string firstName = (catalog/[0]/).data(); + io:println(firstName); // Widget +} +``` + + + + +### Iterating over XML sequences + +Use `foreach` loops or query expressions to process XML sequences in WSO2 Integrator. XML iteration is useful for reading, filtering, and transforming repeating XML elements such as lists of items, records, or orders. + + + + +**Declare the XML payload** + +Add a **Declare Variable** node, name it `x1`, set the type to `xml`, and enter the sample below as the **Expression**: + +``` +xml `Sherlock Holmes +
+ Sir Arthur Conan Doyle + English +
` +``` + +**Add a Foreach node** - // Get all child elements - xml children = catalog/*; +Click **+** and select **Foreach** under **Control**. Set the **Collection** to `x1` and the **Variable** name to `item`. - // Access element text content - xml firstProduct = (catalog/)[0]; - string productName = (firstProduct/).data(); - io:println(productName); // Widget +Iterating over `x1` produces two elements in sequence: - // Access attributes - string? id = (firstProduct).getAttributes()["id"]; - io:println(id); // P1 +1. `Sherlock Holmes` +2. `
Sir Arthur Conan DoyleEnglish
` - // Filter descendant elements - xml names = catalog/**/; - io:println(names); - // WidgetGadget +**Define the loop body** + +Inside the Foreach loop body, add **Declare Variable** nodes to define how each element is processed. For example: + +- Enter `item.data()` to read the combined text content of the current element. +- Enter `item/` to navigate into a named child of the current element. +- Enter `(item).getName()` to get the element tag name. + +
+ + +```ballerina +import ballerina/io; + +public function main() returns error? { + xml x1 = xml `Sherlock Holmes +
+ Sir Arthur Conan Doyle + English +
`; + + // Iterate over the element items in the sequence. + // x1.elements() produces two elements: and
. + foreach xml item in x1.elements() { + io:println(item); + } + // Output (iteration 1): Sherlock Holmes + // Output (iteration 2):
Sir Arthur Conan DoyleEnglish
+ + // Inside the loop body, use navigation and langlib functions to manipulate each element. + foreach xml item in x1.elements() { + string tagName = (item).getName(); + string content = item.data(); + io:println(string `[${tagName}] ${content}`); + } + // Output (iteration 1): [name] Sherlock Holmes + // Output (iteration 2): [details] Sir Arthur Conan DoyleEnglish } ``` + -## XML namespaces +## Transform + +Apply programmatic edits, XSLT stylesheets, and type conversions to reshape XML payloads into the form your integration requires. + +### Modifying XML -Handle namespaced XML using `xmlns` declarations in Ballerina. +You can mutate XML elements in place and concatenate XML sequences to build up payloads incrementally. + +#### Mutating XML elements + +Use `setChildren` to replace the child elements of an element, and `setName` to rename the element itself. Both operations modify the element in place. -:::info -`xmlns` namespace declarations cannot be added through the Visual Designer. Open the Ballerina source file directly and add the `xmlns` bindings at the top of the function or module before using namespace-prefixed expressions in the flow. -::: +**Declare the XML element** -1. **Add namespace declarations in code**: Open the `.bal` file and declare the required namespaces (for example, `xmlns "http://example.com/orders" as ord;`). +Add a **Declare Variable** node, name it `x1`, set the type to `xml:Element`, and enter the sample below as the **Expression**: -2. **Navigate namespaced elements**: Add a **Declare Variable** and use the namespace prefix in the expression (for example, `nsOrder/`). +``` +xml `
+ Sir Arthur Conan Doyle + English +
` +``` + +**Replace child elements with setChildren** + +Click **+** and select **Call Function**. Search for `setChildren` and select it from the `lang.xml` module. Set the target to `x1` and the replacement to the new child XML: + +| Parameter | Value | +|---|---| +| `self` | `x1` | +| `children` | `` xml `French` `` | + +After this call, `x1` holds `
French
`. - ![Flow designer showing Declare Variable for namespaced XML construction and navigation](/img/develop/transform/xml/xml-namespaces-flow.png) +**Rename the element with setName** + +Click **+** and add another **Call Function** step. Search for `setName` and select it. Set the target to `x1` and the new name: + +| Parameter | Value | +|---|---| +| `self` | `x1` | +| `name` | `"updatedDetails"` | + +After this call, `x1` holds `French`.
@@ -195,41 +548,49 @@ Handle namespaced XML using `xmlns` declarations in Ballerina. import ballerina/io; public function main() { - xmlns "http://example.com/orders" as ord; - xmlns "http://example.com/common" as cmn; - - xml nsOrder = xml ` - Acme Corp - 1500.00 - `; - - // Navigate namespaced elements - xml customer = nsOrder/; - io:println(customer); + xml:Element x1 = xml `
+ Sir Arthur Conan Doyle + English +
`; + + // Replace the child elements. + x1.setChildren(xml `French`); + io:println(x1); + // Output:
French
+ + // Rename the element. + x1.setName("updatedDetails"); + io:println(x1); + // Output: French } ``` +
-## Iterating over XML +#### Concatenating XML -Use `foreach` loops or query expressions to process XML sequences in WSO2 Integrator. XML iteration is useful for reading, filtering, and transforming repeating XML elements such as lists of items, records, or orders. +Use the `+` operator to join two XML values into a single sequence. This is useful when building a payload from multiple parts. -1. **Add a Foreach step**: Click **+** and select **Foreach** under **Control**. In the configuration panel, specify the XML collection to iterate over and the loop variable name. +**Declare the two XML values** - | Field | Description | - |---|---| - | **Collection** | The XML sequence to iterate over (for example, `items/`) | - | **Variable** | The loop variable bound to each XML element | +Add two **Declare Variable** nodes: -2. **Process XML elements inside the loop**: Add **Declare Variable** steps inside the loop body to extract values using XML navigation expressions such as `(item/).data()`. +- Name: `x1`, type: `xml`, expression: `` xml `Sherlock Holmes` `` +- Name: `x2`, type: `xml:Element`, expression: `` xml `
Sir Arthur Conan DoyleEnglish
` `` -3. **Use query expressions for filtering**: Add a **Declare Variable** step with a query expression to filter or transform XML sequences based on conditions. +**Concatenate into a new variable** - ![Flow designer showing a Foreach node iterating over XML items with variable extraction steps inside the loop body](/img/develop/transform/xml/xml-iterating-flow.png) +Add a **Declare Variable** node, name it `x3`, set the type to `xml`, and enter `x1 + x2` as the expression. + +The resulting `x3` is a sequence containing both elements: + +```xml +Sherlock Holmes
Sir Arthur Conan DoyleEnglish
+```
@@ -237,100 +598,212 @@ Use `foreach` loops or query expressions to process XML sequences in WSO2 Integr ```ballerina import ballerina/io; -public function main() returns error? { - xml items = xml ` - A13 - B27 - C31 - `; - - // Iterate using foreach - foreach xml item in items/ { - string sku = (item/).data(); - string qty = (item/).data(); - io:println(string `SKU: ${sku}, Quantity: ${qty}`); - } - - // Filter XML elements using query expressions - xml highQty = from xml item in items/ - let string qtyStr = (item/).data() - let int qty = check int:fromString(qtyStr) - where qty > 2 - select item; - io:println(highQty); +public function main() { + xml x1 = xml `Sherlock Holmes`; + xml:Element x2 = xml `
+ Sir Arthur Conan Doyle + English +
`; + + // Use + or .concat() to join two XML sequences. + xml x3 = x1 + x2; + io:println(x3); + // Output: Sherlock Holmes
Sir Arthur Conan DoyleEnglish
} ```
-## XML mutation +### XSLT transformation -Modify XML structures by updating child elements or attributes. XML mutation is useful when transforming payloads, enriching messages, or updating XML content dynamically during integration flows. +Use XSLT to transform XML into another XML, HTML, or plain text format using an XSL stylesheet. The example below transforms a song catalog XML into an HTML table. +WSO2 Integrator supports XSLT version 1.0. -1. **Add a Variable**: Create a **Declare Variable** with the type set to `xml:Element` and initialize it using an XML literal. +**Declare the source XML** -2. **Mutate the XML element**: Click **+** and select **Call Function**. In the right-side panel, search for `setChildren` and select it from the `lang.xml` module. Provide `doc` as the target and the replacement XML literal as the argument. +Add a **Declare Variable** node, name it `sourceXml`, set the type to `xml`, and enter the source XML as the expression: - ![Right-side panel showing the setChildren function search result from the lang.xml module](/img/develop/transform/xml/xml-mutation-function.png) +``` +xml ` + + Summer of 69 + Bryan Adams + Canada + 1984 + + + Zombie + Bad Wolves + USA + 2018 + + ` +``` +**Declare the XSL stylesheet** - ![Flow designer showing a Declare Variable for the XML document followed by a Call Function step for setChildren](/img/develop/transform/xml/xml-mutation-flow.png) +Add another **Declare Variable** node, name it `xsl`, set the type to `xml`, and enter the stylesheet as the expression: - +``` +xml ` + + +

All time favourites

+ + + + + + + + +
TitleArtist
+ +
+
` +``` +**Add the transform step** + +Click **+** and select **Call Function**. In the search box, type `transform xslt` and select **transform** from the results. In the configuration panel, set the fields: + +| Field | Value | +|---|---| +| **Input** | `sourceXml` | +| **Xsl** | `xsl` | +| **Result** | `transformedXml` | +| **Result Type** | `xml` | + +![Visual designer showing two Declare Variable nodes for sourceXml and xsl, with the XSLT transform configuration panel open on the right](/img/develop/transform/xml/xslt-transform.png) + + ```ballerina import ballerina/io; +import ballerina/xslt; -public function main() { - xml:Element doc = xml ` - pending - `; +public function main() returns error? { + xml sourceXml = getXml(); + xml xsl = getXsl(); + xml target = check xslt:transform(sourceXml, xsl); + io:println("Transformed XML: ", target); +} - // Replace child elements - doc.setChildren(xml ` - completed - 2025-01-15 - `); +function getXml() returns xml { + return xml ` + + Summer of 69 + Bryan Adams + Canada + 1984 + + + Zombie + Bad Wolves + USA + 2018 + + `; +} - io:println(doc); +function getXsl() returns xml { + return xml ` + + + +

All time favourites

+ + + + + + + + + + + +
TitleArtist
+ + +
+
`; } ```
-## XML to record conversion +### XML to record -Use the `data.xmldata` module to convert XML data into typed Ballerina records for type-safe access and easier manipulation. Converting XML into records simplifies validation, transformation, and field access within integration flows. +Parse XML into a typed Ballerina record for type-safe field access and easier manipulation inside your integration flow. -1. **Define the target record types**: Navigate to **Types** in the sidebar and click **+** to create type `PurchaseOrder`, `ShipTo` and `Item`, see [Types](../integration-artifacts/supporting/types.md). +**Generate record types from XML** - :::info - The `@xmldata:Attribute` annotation marks a record field as an XML attribute. This annotation cannot be added through the Visual Designer. After creating the `Item` type, open the generated `.bal` file and add `@xmldata:Attribute` manually above the `partNum` field definition. - ::: +In the **Project Overview** panel, click **+** next to **Types**. In the **New Type** panel that opens, switch to the **Import** tab. Set the **Format** to **XML**, paste the sample XML below into the editor, and click **Import**. WSO2 Integrator generates the `PurchaseOrder`, `ShipTo`, and `Item` record types automatically. -2. **Parse XML into the record type**: In the flow designer, click **+** and select **Call Function**. In the right-side panel, search for `parseAsType` and select it from the `data.xmldata` module. +```xml + + + Acme Corp + 123 Main St + Springfield + + + Widget + 10 + 29.99 + + +``` - ![right-side panel showing parseAsType search results with the data.xmldata module entry highlighted](/img/develop/transform/xml/xml-parseAsType.png) +![New Type panel showing the Import tab with XML format selected and the purchase order XML pasted into the editor](/img/develop/transform/xml/import-xml-type.png) - Provide the XML value `product` as the argument and set `PurchaseOrder` as the target record type. +**Declare the XML variable** - ![Flow designer showing the parseAsType function call step with PurchaseOrder as the result type](/img/develop/transform/xml/flow-xml-parse-step.png) +Add a **Declare Variable** node, name it `product`, set the type to `xml`, and enter the same sample XML above as the expression. +**Call parseAsType** - +Click **+** and select **Call Function**. Search for `xml` and select **parseAsType** under the **data.xmldata** section. In the configuration panel, set the fields: + +| Field | Description | Value | +|---|---|---| +| **V** | The source XML value to convert | `product` | +| **T** | The target record type | `PurchaseOrder` | +| **Result** | Name of the result variable | `orders` | +![Visual designer showing the Declare Variable node for the product XML and the parseAsType configuration panel with PurchaseOrder as the target type](/img/develop/transform/xml/parse-as-type.png) + + +Use the following sample XML to generate the record types. In the **Project Overview** panel, click **+** next to **Types**, switch to the **Import** tab, select **XML** as the format, paste the XML below, and click **Import**. + +```xml + + + Acme Corp + 123 Main St + Springfield + + + Widget + 10 + 29.99 + + +``` + ```ballerina import ballerina/data.xmldata; import ballerina/io; @@ -369,30 +842,37 @@ public function main() returns error? { `; - PurchaseOrder orders = check xmldata:parseAsType(po); + PurchaseOrder orders = check xmldata:parseAsType(product); io:println(orders.shipTo.name); // Acme Corp } ``` - - -## Record to XML conversion -Convert Ballerina records into XML using the `data.xmldata` module. Record-to-XML conversion is useful when generating XML payloads for APIs, external systems, or XML-based integrations. + + - - +### Parsing XML from strings, bytes, and streams -1. **Define the record type**: Navigate to **Types** in the sidebar and click **+** to create a new type using xml, see [Types](../integration-artifacts/supporting/types.md). +When XML arrives as a raw string, a byte array, or a byte stream (for example, from an HTTP request body or a file), use the corresponding parse function instead of `parseAsType`: +| Function | Input type | Use when | +|---|---|---| +| `parseString` | `string` | XML is received as a text string | +| `parseBytes` | `byte[]` | XML is received as a byte array | +| `parseStream` | `stream` | XML is received as a streaming byte source | -2. **Convert the record to XML**: In the flow designer, click **+** and select **Call Function**. In the right-side panel, search for `toXml` and select it from the `data.xmldata` module. Provide the `Invoice` record `inv` as the argument and set the result type to `xml`. +All three functions accept the same target record type and return the same typed result as `parseAsType`. The examples below use the `PurchaseOrder` types from the [XML to record](#xml-to-record) section. -![right-side panel showing toXml search results with the data.xmldata module entry highlighted](/img/develop/transform/xml/toXml-function.png) + + -3. **Use the generated XML**: The resulting XML value can be returned from a service, sent to external systems, or further transformed within the integration flow. +Use the `PurchaseOrder` record types generated in the [XML to record](#xml-to-record) section. Click **+** and select **Call Function**. Search for the function name (`parseString`, `parseBytes`, or `parseStream`) and select it under the **data.xmldata** section. In the configuration panel, set the fields: -![Flow designer showing the toXml function call step with xml as the result type](/img/develop/transform/xml/toXml-flow.png) +| Field | Description | Value | +|---|---|---| +| **S** | The source value (string, byte array, or byte stream) | Your source variable | +| **T** | The target record type | `PurchaseOrder` | +| **Result** | Name of the result variable | `orders` | @@ -401,49 +881,65 @@ Convert Ballerina records into XML using the `data.xmldata` module. Record-to-XM import ballerina/data.xmldata; import ballerina/io; -type Invoice record {| - string invoiceId; - string customer; - decimal total; -|}; +// Uses the PurchaseOrder, ShipTo, and Item types from the XML to record section. public function main() returns error? { - Invoice inv = { - invoiceId: "INV-2001", - customer: "Globex Inc", - total: 1500.00 - }; + string xmlString = string ` + + Acme Corp + 123 Main St + Springfield + + + Widget + 10 + 29.99 + + `; + + // Parse from a string + PurchaseOrder fromString = check xmldata:parseString(xmlString); + io:println(fromString.shipTo.name); // Acme Corp - xml invoiceXml = check xmldata:toXml(inv); + // Parse from a byte array + byte[] xmlBytes = xmlString.toBytes(); + PurchaseOrder fromBytes = check xmldata:parseBytes(xmlBytes); + io:println(fromBytes.shipTo.name); // Acme Corp - io:println(invoiceXml); - // INV-2001... + // Parse from a byte stream + stream xmlStream = check io:fileReadBlocksAsStream("order.xml"); + PurchaseOrder fromStream = check xmldata:parseStream(xmlStream); + io:println(fromStream.shipTo.name); // Acme Corp } ``` -## XML to JSON conversion +### XML to JSON -Convert XML data to JSON by first parsing the XML into a typed record using the `data.xmldata` module, then converting the record to JSON using the built-in `toJson()` method from `lang.value`. To convert JSON back to XML, use `xmldata:fromJson` from `data.xmldata`. These conversions are useful when integrating XML-based systems with JSON-based APIs and services. +Converting XML to JSON is a two-step process: first parse the XML into a typed record, then convert the record to JSON using `toJson()`. -1. **Define the record types**: Navigate to **Types** in the sidebar and click **+** to create each type from scratch. Define the source record with fields matching your XML structure and the target record with fields required for the JSON output. For more information, see [Types](../integration-artifacts/supporting/types.md). +**Step 1: Convert XML to a record** +Follow the steps in [XML to record](#xml-to-record) to parse your XML value into a typed record. -2. **Parse XML into a record**: Follow the steps in [XML to record conversion](#xml-to-record-conversion) to parse the XML value into a typed record. +**Step 2: Convert the record to JSON** -3. **Convert the record to JSON**: Click **+** and select **Call Function**. In the right-side panel, search for `toJson` and select it from the `lang.value` module. Provide the parsed record as the argument. The return value is a `json` value. +Click **+** and select **Call Function**. Search for `toJson` and select **toJson** under the **lang.value** section. In the configuration panel, set the fields: - ![Flow designer showing the XML parse, mapOrder, and toJson return steps in sequence](/img/develop/transform/xml/xml-to-json.png) +| Field | Description | Value | +|---|---|---| +| **V** | The record value to convert | `orders` | +| **Result** | Name of the result variable | `ordersJson` | +| **Result Type** | Type of the result variable | `json` | -4. **Map fields visually**: Use the [Visual Data Mapper](../integration-artifacts/supporting/data-mapper/data-mapper.md) to map or transform fields between record structures before converting the result into JSON. +![Visual designer showing the parseAsType and toJson steps with the lang.value toJson configuration panel open on the right](/img/develop/transform/xml/to-json.png) - ```ballerina @@ -456,22 +952,198 @@ public function main() returns error? { info@acme.com `; - // Convert XML to a typed record + // Parse XML into a typed record. record {| string name; string email; |} customer = check xmldata:parseAsType(customers); - // Convert record to JSON + // Convert the record to JSON. json customerJson = customer.toJson(); io:println(customerJson); - // Convert JSON back to XML + // Convert JSON back to XML. xml result = check xmldata:fromJson(customerJson); io:println(result); } ``` + + + + +### Record to XML + +Convert a Ballerina record into XML when generating XML payloads for external systems or XML-based integrations. + + + + +**Create the record types** + +Navigate to **Types** in the sidebar and click **+** to create the following two record types. For more information, see [Types](../integration-artifacts/supporting/types.md). + +Create an `Item` type with these fields: + +| Field name | Type | +|---|---| +| `itemCode` | `string` | +| `count` | `int` | + +Create an `Invoice` type with these fields: + +| Field name | Type | Optional | +|---|---|---| +| `id` | `int` | No | +| `items` | `Item[]` | No | +| `xmlns` | `string` | No | +| `status` | `string` | Yes | + +**Add annotations in the source** + +Open the generated `.bal` file for the types and add `xmldata` annotations to control namespaces and which fields become XML attributes. Replace the generated type definitions with the complete annotated versions from the **Ballerina Code** tab. + +**Declare the record variable** + +Add a **Declare Variable** node, name it `data`, set the type to `Invoice`, and enter the record value as the expression: + +``` +{id: 1, items: [{itemCode: "223345", count: 1}, {itemCode: "223300", count: 7}], status: "paid"} +``` + +**Call toXml** + +Click **+** and select **Call Function**. Search for `toXml` and select **toXml** under the **xmldata** section. In the configuration panel, set the fields: + +| Field | Description | Value | +|---|---|---| +| **Map Value** | The record value to convert | `data` | +| **Result** | Name of the result variable | `result` | +| **Result Type** | Type of the result variable | `xml` | + +![Visual designer showing the Declare Variable node for the Invoice data and the xmldata toXml configuration panel open on the right](/img/develop/transform/xml/to-xml.png) + + + + +```ballerina +import ballerina/io; +import ballerina/xmldata; + +@xmldata:Namespace { + prefix: "ns", + uri: "http://sdf.com" +} +type Invoice record { + int id; + Item[] items; + @xmldata:Attribute + string 'xmlns = "example.com"; + @xmldata:Attribute + string status?; +}; + +@xmldata:Namespace { + uri: "http://example1.com" +} +type Item record { + string itemCode; + int count; +}; + +public function main() returns error? { + Invoice data = { + id: 1, + items: [ + {itemCode: "223345", count: 1}, + {itemCode: "223300", count: 7} + ], + status: "paid" + }; + + xml result = check xmldata:toXml(data); + io:println(result); +} +``` + + + + + +## Validate + +Verify incoming XML payloads against schema definitions and handle XML-specific errors to keep your integration flows reliable. + +### XSD schema validation + +Validate XML payloads against an XSD schema to enforce structural contracts at integration boundaries. Schema validation is most useful when consuming payloads from external partners, exposing XML-based APIs, or enforcing data integrity before processing. + + + + +1. **Choose a schema source**: The `validate` function accepts the schema in either of two forms: + + - An `.xsd` file. Place the file in the `resources` directory of your integration project (for example, `resources/order-schema.xsd`). WSO2 Integrator includes project resources when building the integration. + - A Ballerina record type that represents the schema. Generate the type from your `.xsd` file with the XSD tool. For more information, see [Generating record types from XSD](../tools/integration-tools/xsd-tool.md#generating-record-types-from-xsd). + +2. **Add a Call Function step**: In the visual designer, click **+** and select **Call Function**. In the search bar, search for `xsd` and select the **validate** node under the **data.xmldata** section. + +3. **Configure the validation inputs**: + + | Field | Description | + |---|---| + | **Xml Value** | The `xml` value to validate | + | **Schema** | The `.xsd` file path or a record type that represents the schema | + | **Result** | A variable to capture the validation outcome (`()` on success, `xmldata:Error` on failure) | + +4. **Handle the result**: Add a conditional step after the function call. If the XML conforms to the schema, `validate` returns `()` and the flow continues. If validation fails, it returns an `xmldata:Error` with details about the schema violation. + + + + +The `schema` parameter accepts either a file path to an `.xsd` file or a Ballerina record type that represents the schema. The function returns `()` when the XML conforms to the schema, or an `xmldata:Error` otherwise. + +```ballerina +import ballerina/data.xmldata; +import ballerina/io; + +// A record type can serve as the schema. Each field maps to an expected element. +type Order record {| + string orderId; + string customer; + decimal total; +|}; + +public function main() { + xml validOrder = xml ` + ORD-5001 + Acme Corp + 250.00 + `; + + xml invalidOrder = xml ` + Acme Corp + 250.00 + `; + + // Validate against an .xsd file in the project resources. + xmldata:Error? fileResult = xmldata:validate(validOrder, "resources/order-schema.xsd"); + if fileResult is () { + io:println("Validation passed"); + } else { + io:println("Validation failed: ", fileResult.message()); + } + + // Validate against a Ballerina record type instead of an .xsd file. + xmldata:Error? typeResult = xmldata:validate(invalidOrder, Order); + if typeResult is xmldata:Error { + io:println("Validation failed: ", typeResult.message()); + // Example output: cvc-complex-type.2.4.a: Invalid content was found + // starting with element 'customer'. One of '{orderId}' is expected. + } +} +``` + @@ -479,5 +1151,6 @@ public function main() returns error? { ## What's next - [JSON Processing](json.md) - Parse, construct, transform, and validate JSON data +- [XSD Tool](../tools/integration-tools/xsd-tool.md) - Generate Ballerina record types from an XSD schema - [Visual Data Mapper](../integration-artifacts/supporting/data-mapper/data-mapper.md) - Map fields between record types visually - [Types](../integration-artifacts/supporting/types.md) - Define record types for type-safe data handling diff --git a/en/static/img/develop/transform/xml/data-mapper-xml-to-record.png b/en/static/img/develop/transform/xml/data-mapper-xml-to-record.png deleted file mode 100644 index 420d0104bcb..00000000000 Binary files a/en/static/img/develop/transform/xml/data-mapper-xml-to-record.png and /dev/null differ diff --git a/en/static/img/develop/transform/xml/dynamic-xml.png b/en/static/img/develop/transform/xml/dynamic-xml.png new file mode 100644 index 00000000000..c55f4814477 Binary files /dev/null and b/en/static/img/develop/transform/xml/dynamic-xml.png differ diff --git a/en/static/img/develop/transform/xml/flow-xml-parse-step.png b/en/static/img/develop/transform/xml/flow-xml-parse-step.png deleted file mode 100644 index 3c04e06f965..00000000000 Binary files a/en/static/img/develop/transform/xml/flow-xml-parse-step.png and /dev/null differ diff --git a/en/static/img/develop/transform/xml/import-xml-type.png b/en/static/img/develop/transform/xml/import-xml-type.png new file mode 100644 index 00000000000..f2df656fd92 Binary files /dev/null and b/en/static/img/develop/transform/xml/import-xml-type.png differ diff --git a/en/static/img/develop/transform/xml/parse-as-type.png b/en/static/img/develop/transform/xml/parse-as-type.png new file mode 100644 index 00000000000..d188284b15b Binary files /dev/null and b/en/static/img/develop/transform/xml/parse-as-type.png differ diff --git a/en/static/img/develop/transform/xml/to-json.png b/en/static/img/develop/transform/xml/to-json.png new file mode 100644 index 00000000000..d9c97f861aa Binary files /dev/null and b/en/static/img/develop/transform/xml/to-json.png differ diff --git a/en/static/img/develop/transform/xml/to-xml.png b/en/static/img/develop/transform/xml/to-xml.png new file mode 100644 index 00000000000..d1766d25222 Binary files /dev/null and b/en/static/img/develop/transform/xml/to-xml.png differ diff --git a/en/static/img/develop/transform/xml/toXml-flow.png b/en/static/img/develop/transform/xml/toXml-flow.png deleted file mode 100644 index f315ad0e376..00000000000 Binary files a/en/static/img/develop/transform/xml/toXml-flow.png and /dev/null differ diff --git a/en/static/img/develop/transform/xml/toXml-function.png b/en/static/img/develop/transform/xml/toXml-function.png deleted file mode 100644 index 78bdea2ed1c..00000000000 Binary files a/en/static/img/develop/transform/xml/toXml-function.png and /dev/null differ diff --git a/en/static/img/develop/transform/xml/types-import-tab.png b/en/static/img/develop/transform/xml/types-import-tab.png deleted file mode 100644 index 02e7d053e70..00000000000 Binary files a/en/static/img/develop/transform/xml/types-import-tab.png and /dev/null differ diff --git a/en/static/img/develop/transform/xml/xml-iterating-flow.png b/en/static/img/develop/transform/xml/xml-iterating-flow.png deleted file mode 100644 index 7b499fc9977..00000000000 Binary files a/en/static/img/develop/transform/xml/xml-iterating-flow.png and /dev/null differ diff --git a/en/static/img/develop/transform/xml/xml-literals-dynamic-panel.png b/en/static/img/develop/transform/xml/xml-literals-dynamic-panel.png deleted file mode 100644 index 790fd7d7173..00000000000 Binary files a/en/static/img/develop/transform/xml/xml-literals-dynamic-panel.png and /dev/null differ diff --git a/en/static/img/develop/transform/xml/xml-literals-flow.png b/en/static/img/develop/transform/xml/xml-literals-flow.png deleted file mode 100644 index 4e97b464c32..00000000000 Binary files a/en/static/img/develop/transform/xml/xml-literals-flow.png and /dev/null differ diff --git a/en/static/img/develop/transform/xml/xml-mutation-flow.png b/en/static/img/develop/transform/xml/xml-mutation-flow.png deleted file mode 100644 index 8522fee7049..00000000000 Binary files a/en/static/img/develop/transform/xml/xml-mutation-flow.png and /dev/null differ diff --git a/en/static/img/develop/transform/xml/xml-mutation-function.png b/en/static/img/develop/transform/xml/xml-mutation-function.png deleted file mode 100644 index 474e741fab8..00000000000 Binary files a/en/static/img/develop/transform/xml/xml-mutation-function.png and /dev/null differ diff --git a/en/static/img/develop/transform/xml/xml-namespaces-flow.png b/en/static/img/develop/transform/xml/xml-namespaces-flow.png deleted file mode 100644 index b5ccac31ba8..00000000000 Binary files a/en/static/img/develop/transform/xml/xml-namespaces-flow.png and /dev/null differ diff --git a/en/static/img/develop/transform/xml/xml-navigating-child-panel.png b/en/static/img/develop/transform/xml/xml-navigating-child-panel.png deleted file mode 100644 index 202d2822e48..00000000000 Binary files a/en/static/img/develop/transform/xml/xml-navigating-child-panel.png and /dev/null differ diff --git a/en/static/img/develop/transform/xml/xml-navigating-flow.png b/en/static/img/develop/transform/xml/xml-navigating-flow.png deleted file mode 100644 index f9768f3bb64..00000000000 Binary files a/en/static/img/develop/transform/xml/xml-navigating-flow.png and /dev/null differ diff --git a/en/static/img/develop/transform/xml/xml-ns.png b/en/static/img/develop/transform/xml/xml-ns.png new file mode 100644 index 00000000000..6604e355e0d Binary files /dev/null and b/en/static/img/develop/transform/xml/xml-ns.png differ diff --git a/en/static/img/develop/transform/xml/xml-parseAsType.png b/en/static/img/develop/transform/xml/xml-parseAsType.png deleted file mode 100644 index f2fad49209d..00000000000 Binary files a/en/static/img/develop/transform/xml/xml-parseAsType.png and /dev/null differ diff --git a/en/static/img/develop/transform/xml/xml-payload.png b/en/static/img/develop/transform/xml/xml-payload.png new file mode 100644 index 00000000000..baa0cd21385 Binary files /dev/null and b/en/static/img/develop/transform/xml/xml-payload.png differ diff --git a/en/static/img/develop/transform/xml/xml-text-comments-flow.png b/en/static/img/develop/transform/xml/xml-text-comments-flow.png deleted file mode 100644 index 3731eef7aa0..00000000000 Binary files a/en/static/img/develop/transform/xml/xml-text-comments-flow.png and /dev/null differ diff --git a/en/static/img/develop/transform/xml/xml-to-json.png b/en/static/img/develop/transform/xml/xml-to-json.png deleted file mode 100644 index 6ff60ee2715..00000000000 Binary files a/en/static/img/develop/transform/xml/xml-to-json.png and /dev/null differ diff --git a/en/static/img/develop/transform/xml/xslt-transform.png b/en/static/img/develop/transform/xml/xslt-transform.png new file mode 100644 index 00000000000..7975da1fd92 Binary files /dev/null and b/en/static/img/develop/transform/xml/xslt-transform.png differ