Saturday, December 16, 2017

Mule Dev Tricks: Dataweave Working With XML Data

The Use Case

In my earlier post, I have explained the tricks using xpath3() to extract data from xml data. In this post I will demonstrate a use case to extract data using Dataweave.

Here is the input data (I omitted the not-interesting xml data:
...
            
                        
                            anshul.gupta277@gmail.com
                            
                                
                                    
                                        836cf00ef5974ac08b786079866c946f
                                        HOME
                                    
                                
                            
                        
                        
                            sudeshna.nanda@tcs.com
                            
                                
                                    
                                        1f27f250dfaa4724ab1e1617174281e4
                                        WORK
                                    
                                
                            
                        
            
...

The desired output is an arraylist:

[
     {
         email=anshul.gupta277@gmail.com, 
         wid=836cf00ef5974ac08b786079866c946f, 
         type=HOME
     }, 
     {
         email=sudeshna.nanda@tcs.com, 
         wid=1f27f250dfaa4724ab1e1617174281e4, 
         type=WORK
     }
]

The Solution

The following is the solution:
%dw 1.0
%output application/java
%namespace ns0 urn:com.workday/bsvc
---
payload.ns0#Get_Workers_Response.ns0#Response_Data.*ns0#Worker.ns0#Worker_Data.ns0#Personal_Data.ns0#Contact_Data.*ns0#Email_Address_Data
map {
 email: $.ns0#Email_Address,
 wid: $.ns0#Usage_Data.ns0#Type_Data.ns0#Type_Reference.*ns0#ID[?($.@ns0#type == "WID")][0],
 type: $.ns0#Usage_Data.ns0#Type_Data.ns0#Type_Reference.*ns0#ID[?($.@ns0#type == "Communication_Usage_Type_ID")][0]
}

The Key Learning Points

  1. Make array with wildcard *: ns0#Contact_Data.*ns0#Email_Address_Data
  2. Get expect element using: ns0#ID[?($.@ns0#type == "WID")][0]
  3. Remember: access attribute of a xml element using @

Monday, May 29, 2017

Mule Application Development - Advanced Topics (1): Global Functions

Introduction

In my previous post, I have talked about using Data-weave functions. Now, you will see that you may use those functions in different places. Sometimes, you may need to use the same function in MEL. For these kind of situations, the global function will resolve the reuse of functions. In this post, I am going to demonstrate how to you define these functions and how to use them.

Define Global Functions

Global functions can be defined any where in mule configurations files. However, it is better to define in a specific file. Here I have a file named: common.xml with the following contents:





     
         
             
             
             
              
                 def getReasonId2(reason) {
                   return StringUtils.splitAndTrim(reason, "-")[1];
                 } 
                 
                 def normalizeDate (aDateString) {
                  date = new DateTime(aDateString, 'MM/dd/yyyy');
             date.format('yyyy-MM-dd');
             return date;
                 }
                 
                 def mergeArray(data) {
                  sValue = StringUtils.join(data,',');
                  return sValue
                 }
              
            
         
     

In the above code, I defined 3 functions, getReasonId2, normalizeDate, and mergeArray. These functions use class of org.mule.util.StringUtils and org.mule.el.datetime.DateTime. These are imported before the global-functions element. As you can see, we are using classes from other packages. you can develop your own java classes and imported here too. We will demonstrate that in the next blog. For now, let demonstrate how to use them. Actually, the usage is the same as we use in data-weave functions. Mule runtime will be able to figure out where it is defined.

Usages




    
        
        
             {
 staff_id: record."Buyer Reference",
 Contract_End_Date: normalizeDate(record."End Date") as :date,
 Last_Day_of_Work: normalizeDate(record."End Date") as :date,
 termination_reason: getReasonId2(record."Closed Reason")
})]]>
        
        
        
    


Sample Input

[
  {
    "Buyer Reference": "207157",
    "Start Date": "05/01/2017",
    "End Date": "05/01/2017",
    "Closed Reason": "Voluntary - Assignment Completed"
  },
   {
    "Buyer Reference": "207157",
    "Start Date": "05/01/2017",
    "End Date": "05/01/2017",
    "Closed Reason": "Voluntary - Assignment Completed"
  }
]

Output From Sample Input

[
  {
    "staff_id": "207157",
    "Contract_End_Date": "2017-05-01",
    "Last_Day_of_Work": "2017-05-01",
    "termination_reason": "Assignment Completed"
  },
  {
    "staff_id": "207157",
    "Contract_End_Date": "2017-05-01",
    "Last_Day_of_Work": "2017-05-01",
    "termination_reason": "Assignment Completed"
  }
]

The above code demonstrates the usage of global functions in data-weave and MEL expression.

Further Improvement

The global-functions can be defined in a file. In this way, we separate the mule configuration files with global functions definition files. The following is the improved configuration file:




     
         
             
             
                 
            
         
     

Put my_global_functions.mven under src/main/resources, or any places within classpath.

Tuesday, May 23, 2017

Mule Dev Tricks: Create An Array List From JSON Using DataWeave

Input

[
 {
  "id" : "11111",
  "name" : "AAAAA"
 },
 {
  "id" : "22222",
  "name": "BBBBB"
 },
 {
  "id" : "33333",
  "name" : "CCCCC"
 }
] 

Output

["11111", "22222", "33333"]
As you can see, we only want to extract the id from the input.

DataWeave Script

%dw 1.0
%output application/java
---
payload map (data) -> data.id

The map function from the Dataweave component is the mostly used function. Here we using a simple lamda expression to extract id. Another simple way is:
payload map $.id  

Tuesday, May 9, 2017

Mule Dev Tricks: Transform Dates Using Datawave Functions

Introduction

Processing dates is very common practice in data transformation. Dataweave's function makes this task very easy. This short blog demonstrate the procedures to do so.

Normalizing Dates

As shown in the following Dataweave code. The function normalize replace "/" and "." with "-". Note: we can use replace in a concatenation style.
%dw 1.0
%output application/json
%function normalize(date) date replace "/" with "-" replace "." with "-"
---
{
 dates : {
  date1: normalize("26-JUL-16") as :date {format: "dd-MMM-yy"} as :string {format: "yyyy-MM-dd"} as :date,
  date2: normalize("26/JUL/16") as :date {format: "dd-MMM-yy"} as :string {format: "yyyy-MM-dd"} as :date,
  date3: normalize("26.JUL.16") as :date {format: "dd-MMM-yy"} as :date {format: "yyyy-MM-dd"}
   }
}
In the above code, I provide two flavors for transforming the dates to the format of "yyyy-MM-dd". Apparently, date3 is a bit cleaner. The above code can be simplified by adding the data format in the function body:
%dw 1.0
%output application/json
%function normalize(date) date replace "/" with "-" replace "." with "-" as :date {format: "dd-MMM-yy"} as :date {format: "yyyy-MM-dd"}
---
{
 dates : {
  date1: normalize("26-JUL-16"),
  date2: normalize("26/JUL/16"),
  date3: normalize("26.JUL.16") 
   }
}
As you can see, now the Data-weave code is much cleaner.

Wednesday, May 3, 2017

Mule Dev Tricks: DataWeave - Using mapObject - Part IV

Introduction

In my last post I covered the topic of removing null object and empty strings from JSON payload. That solution is only good for two level of objects. If the JSON hierarchy has multiple level, then it would not work. In that case, we need to use recursion. This post will show the solution for this purpose.

Sample Input

[
  {
    "first_name" : "Gary",
    "middle_name" : "",
    "last_name" : "Liu",
    "address" : {
     "street" : {
      "number" : "",
      "name" : ""
     },
     "city" : null,
     "zip" : null
    }
  }
]

Desired Output

[
  {
    "first_name": "Gary",
    "last_name": "Liu"
  }
]

Solution

%dw 1.0
%output application/json

%function skipNulls(o) o 
    unless o is :object 
    otherwise o mapObject {
        (($$): skipNulls($)) when ($ != null and $ != "")
    }

%function skipEmpty(o) o
 unless o is :object 
    otherwise o mapObject {
        (($$): skipEmpty($)) when ($ != {} and $ != '')
    }

---
payload map ((person)  -> { 
 (skipEmpty(person mapObject { 
         ($$): skipNulls($)
     })
 )
 
})

Key Learning Points

In the solution given here, I used the recursion as the following:
%function skipNulls(o) o 
    unless o is :object 
    otherwise o mapObject {
        (($$): skipNulls($)) when ($ != null and $ != "")
    }
This demonstrates that dataweave functions allow us to use the recursion concept. The code provide here is not perfect yet. There is a bug I am working on.

Anypoint Studio Error: The project is missing Munit lIbrary to run tests

Anypoint Studio 7.9 has a bug. Even if we following the article: https://help.mulesoft.com/s/article/The-project-is-missing-MUnit-libraries-...