Mule LogoThis is the second part of a two part blog series about the new capabilities of the Dataweave Tree and Values modules provided in Mule Runtime 4.2.2. As we have seen in Part 1, these modules can help us create more robust Dataweave scripts. They work by traversing the input payload while operating on leaf values of the payload with autonomous functions that you define.  Although the Values modules can do some of what you can do with the Tree Module, the Tree module has an additional parameter, i.e. the path object, that can give you a lot more flexibility.

 

 

Trees, Branches and Leaves

To understand how the tree module works, we have to explain some concepts around data object models as they pertain to XML, JSON or Java-based payloads. These payloads can be broken down into object models or graphs that consist of simple nesting of parent and child nodes. If you imagine an upside down tree, the document or payload structure can be thought of having a trunk, (or in XML terminology we refer to it as the root), with branches and leaves. This diagram illustrates how the Tree module interprets the nodes like a tree.
 

Document Tree

 

Nodes in the tree are marked with hash marks, “#” for branches or leaf symbols for leaves. Notice how nodes in the input payload connect to other nodes, like branches and leaves in a tree. 

Nodes that contain simple data types  like integers, decimal values, dates and strings are like leaves on a tree,  i.e. they cannot sprout more branches since they just contain data. To get to the leaf node named “number” you have to traverse through a series of branches or nodes of different types. The path of nodes needed to get to the leaf is what is contained in the path object and used in the Tree module.

 

The PathElement Object Type

The path value is an array of type PathElement and has the following definition:
 
Type PathElement =
 {
      kind: [“Object”,”Attribute”,”Array”], 
      selector: String | Number, 
      namespace: Namespace | null
    }

When the tree module method, mapLeafValues for instances, reaches the “number” leaf node, the path array looks like this:                                        

 {
    “kind”:“Object”,
    “selector”:“payment-info”,
    “namespace”null
 },
 {
    “kind”:“Array”,
    “selector”0,
    “namespace”null

  },
 {
   “kind”:“Object”,
   “selector”:“number”,
   “namespace”null
 }            

 

In the JSON example above, to get to the number element, the tree module would have traversed through the payment-info node, an array element node, and the number node. The number node is a leaf node type. It is a leaf because it contains a string value. 

In an XML document the first node is the root of the document. From the root, we can branch off with sub-elements and attributes. Attributes as well as elements of an XML document are nodes. Attributes are always leaves, since they will always contain simple data types. Elements however can be branches or leafs, depending on what it contains. If the element contains only simple numeric values, dates or a string, it will be a leaf. If it contains one or more child elements, it will be a branch. If we are navigating down an XML document, the namespace would contain the namespaces of the elements. Since JSON doesn’t use namespaces the values are null.

 

Tree ModuleTree

The Tree module has three methods, the first one we will not address since the use cases are little different than what we are addressing here, namely modifying payload leaf values and keeping the structure of the initial payload. The other two, mapLeafValues and nodeExists, are applicable. We already showed one example for mapLeafValues, hiding credit card numbers, in Part 1. Here we will show how it can be used to format data. We will also demonstrate how nodeExists can be used.

  • asExpressionString(Path):String
  • mapLeafValues(Any, (value: Any, path: Path) -> Any): Any
  • nodeExists(Any, (value: Any, path: Path) -> Boolean): Boolean

Before we dive into more details, it is important to note that the Tree and Values modules will only execute the autonomous functions on leaf nodes. It will not call the autonomous functions if the node contains complex objects or array elements. Formatting data with mapLeafValues, we will build on the simple task of hiding credit card numbers but we will also format values on many different leaves in the document at the same time. 

 

Formatting Data with mapLeafValues

In this example, we will build on the simple task of hiding credit card numbers by also formatting data on many different leafs in the document at the same time.  Starting with the familiar input payload.

{ “name”:“John Doe”,
  “address”:“123 State St, Auburn MA 01824”,
  “payment-info”:[
    {“type”:“CC”,
     “number”:“1231-1123-1231-1233”,
     “amount”12.10
    },
    {“type”:“GIFT_CARD”,
     “number”:“abc-123-def”,
     “amount”34.211
    }]
}

 

We now create a more complex Dataweave script with the Tree.mapLeafValues module. This one obfuscates the credit card number, formats enumerated values into human readable form and formats decimal numbers as currency values.

%dw 2.0
output application/json
import * from dw::util::Tree
var paymentTypes = [{“key”:“CC”,“value”:“Credit Card”},
                                     {“key”:“GIFT_CARD”,“value”:“Gift Card”}]
fun convert(fromVal) = (paymentTypes[?(contains($..key,fromVal))]
                  default [{“value”:fromVal}])[0]
type Currency = String{ format: “$#,###.00”}

payload mapLeafValues (value, path)  -> 
   if (path[-1].selector==“number”)  (“*******“)
      else if (path[-1].selector==“type”)  (convert(value).value)
   else if (path[-1].selector==“amount”) value as Currency
      else value

 

The output of the script is shown here.

{ “name”:“John Doe”,
  “address”:“123 State St, Auburn MA 01824”,
  “payment-info”:[
    {“type”:“Credit Card”,
     “number”:“*******”,
     “amount”“$12.10”
    },
    {“type”:“Gift Card”,
     “number”:“*******”,
     “amount”: “$32.21”
    }]
}

 

Notice here that all three values for type, number and amount have changed. The enumerated type was transformed to a more readable format, the credit card and gift card numbers are hidden and the amount was formatted as a currency. 

 

Checking content with nodeExistsTrue False

The last example uses the nodeExists method to test for certain content in the payload. For example, if we want to know if any gift cards are in the payload, we could use the following Dataweave script with the Tree.nodeExists method.

%dw 2.0
output application/json
import * from dw::util::Tree

payload nodeExists (value, path)  -> 
   (path[-1].selector==“type” and value == “GIFT_CARD”)

The output is a simple boolean value, true or false.  

true

 

Other Considerations for Using Tree.mapLeafValues

Because the path contains all elements from the top-most element down to the current location, you can hide all information in an object or array by selecting its parent node, payment-info. Starting with the same input.

{ “name”:“John Doe”,
  “address”:“123 State St, Auburn MA 01824”,
  “payment-info”:[
    {“type”:“CC”,
     “number”:“1231-1123-1231-1233”,
     “amount”12.10
    },
    {“type”:“GIFT_CARD”,
     “number”:“abc-123-def”,
     “amount”34.211
    }]
}

 

We create a Dataweave script using the mapLeafValues to hide all leaf values from the main branch.

%dw 2.0
output application/json
import * from dw::util::Tree

payload mapLeafValues (value, path)  -> 
   if (contains(path.*selector==“payment-info”)  (“*******“)
      else value

 

The output of the script is shown here.

{ “name”:“John Doe”,
  “address”:“123 State St, Auburn MA 01824”,
  “payment-info”:[
    {“type”:“*******”,
     “number”:“*******”,
     “amount”“*******”
    },
    {“type”:“*******”,
     “number”:“*******”,
     “amount”: “*******”
    }]
}

 

Conclusions

The Tree and Value modules are a welcome addition to the Dataweave toolkit. As you haveGreen Check Mark seen, the Values module is easier to use, but the Tree module gives you more overall control and functionality. They both can be used to build robust scripts so that changes will not easily break the code or make the code stale. This can be especially important if new values are added to the payload or the structure of the payload changes. Without them we may have had to implement scripts with specific features of the payload in mind.

 

Other Helpful Dataweave Links

Join the Conversation

About the Author