Using the Parse Template transformer with an array in the data - mule

I'm a newbie to Mulesoft, and I'm looking to transform a Json object into an XML object via the Parse Template transformer. Straight forward enough, however, what is the appropriate approach when the Json object has an array with an undetermined number of elements?
For example, if I have a JSON object that looks like
{ "name" : "Methusela",
"hobbies: [
"name": "guitar",
"name": "cooking",
"name": "someting",
...
]}
where the number of hobbies is unknown at runtime
and my xml template looks like:
<Person>
<Name>#[payload.name]</Name>
<Hobbies>
<!- What should this bit look like -->
</Hobbies>
So - my question is how should I approach the conversion of the open ended JSON array to XML?
Note that we are currently exploring the community edition and therefore don't have access to the data mapping functionality available through the enterprise edition.
Thanks

Personally, to transform a Json object into an XML object, by using Community edition, I will use: JSON to Object and Object to XML transformers.
However, to transform it using Parse Template I will utilize Mule Expression Language (MEL). For example:
<Person>
<Name>#[json:name]</Name>
<Hobbies>
#[
org.json.JSONObject jsonObject = new org.json.JSONObject(payload.toString());
org.json.JSONArray jsonArray = jsonObject.get("hobbies");
String hobbies = "";
for(int i = 0; i < jsonArray.length(); i++) {
hobbies += "<Name>" + jsonArray.get(i).getString("name") + "</Name>";
}
hobbies;
]
</Hobbies>
</Person>

In parse template you can use simple javascript loop:
for(i=0;i<#[payload.Hobbies].length;i++){
document.getElementById('Id').innerHtml += #[payload.Hobbies][i]
}

Related

how to reuse the xpr file in Mule 4

In my case, I'm doing a migration from Mule 3 to Mule 4.
This flow, which includes transformers like DOM to XML, XML to DOM, and an expression component, needs to be migrated.
In Mule 4, I want to reuse the xrp file.
My flow for XML payload transform uses an XPR file by the expression component in Mule 3.
<flow name="rate-dtostepFlow">
<http:listener config-ref="HTTP_Listener_Configuration" path="/dtostep" allowedMethods="POST" doc:name="HTTP"/>
<mulexml:xml-to-dom-transformer returnClass="org.dom4j.Document" doc:name="XML to DOM"/>
<set-variable variableName="domPayload" value="#[payload]" doc:name="set domPayload "/>
<expression-component file="xpr/responseStubCannasure.xpr" doc:name="Expression"/>
<mulexml:dom-to-xml-transformer doc:name="DOM to XML"/>
</flow>
Input XML: https://github.com/Manikandan99/rate-dtostep/blob/master/request.xml
Output XML: https://github.com/Manikandan99/rate-dtostep/blob/master/response.xml
my MULE 3 application : https://github.com/Manikandan99/rate-dtostep/tree/master/rate-dtostep
ResponseStubcannsure xpr file:
import org.dom4j.*;
import java.util.*;
import java.util.logging.Logger;
Logger logger = Logger.getLogger("");
dtoCoverageStepsNodes = flowVars.domPayload.selectNodes("//DTOCoverage[#Status=\'Active\']/DTOSteps");
for (Node node : dtoCoverageStepsNodes){
//logger.info("inside: detach");
node.detach();
}
dtoCoverageNodes = flowVars.domPayload.selectNodes("//DTOCoverage[#Status=\'Active\']");
int i = 500;
for (Node node : dtoCoverageNodes){
//node.detach();
//logger.info("inside: assign prem");
node.addAttribute("FullTermAmt", Integer.toString(i));
node.addElement("DTOSteps");
stepNode = node.selectSingleNode("DTOSteps");
stepNode.addElement("DTOStep")
.addAttribute("Order","1")
.addAttribute("Name","Final Premium")
.addAttribute("Desc","Final Premium Desc")
.addAttribute("Operation","=")
.addAttribute("Factor",Integer.toString(i))
.addAttribute("Value",Integer.toString(i));
i+=1;
}
The xpr file transform the xml payload in the following ways:
updated the value of the DTOStep node.
The attribute value of DTOStep is autoincremented from 500 each time.
Please assist me.
You need to migrate the complete flow to Mule 4. The file responseStubCannasure.xpr is just a script in MEL (Mule 3 expression language). The extension is irrelevant, it could have been anything. MEL is very similar to Java so you could reuse the logic by encapsulating it into a Java class. You will need to add to the Java code the conversion to DOM4J from the input XML because Mule 4 doesn't support it. Probably is slightly easier to migrate the MEL script to a Groovy script because the basic syntax is very similar and both support scripts. Migrating to Java is just taking the additional steps of encapsulating the script into a method of a class and defining explicitly the types for variables.
Alternatively you could just delete the last 4 operations of the flow and replace them with a DataWeave transformation. Using a recursive function to navigate the keys and value recursively, using a condition to check if we are in the element DTOCoverage, with attribute Status == "Active" and then replace the nested element with the DTOSteps/DTOStep combination. Which is what your script does.
Example:
%dw 2.0
output application/xml
var startingValue=500
fun transformSteps(x, index)=
x filterObject ($$ as String != "DTOSteps") ++
{
DTOSteps: DTOStep #(Order:1, Factor: index + startingValue, Value: index + startingValue, Name:"Final Premiun", Operation:"=", Desc: "Final Premium Desc"): null
}
fun transformCoverage(x, index)=
{
val: x match {
case is Object -> x mapObject
if ($$ as String == "DTOCoverage" and $$.#Status == "Active")
{
DTOCoverage #(( $$.# - "FullTermAmt" ), FullTermAmt: $$$ + startingValue):
transformSteps($, index)
}
else
(($$): transformCoverage($, index+1))
else -> $
},
index: index
}
---
transformCoverage(payload,1).val
This solution doesn't completely resolve the Value and Factor (why two attributes with the same value?) sequential increase. You may need to do an additional transformation, or use Groovy or Java code to renumber them.

POSTMAN: Extracting Values from body

I'm trying to recreate a scenario with the postman and there is a _csrf value in the previous GET request response body to be passed with the next POST request.
I Can't find a way to extract the value from POSTMAN.
NOTE: What I want is something similar to Regular Expression Extractor in Jmeter.If you have any Idea about extracting a value form the response body and setting it to a variable. Please let me know.
Cheers,
Muditha
This might help you https://media.readthedocs.org/pdf/postman-quick-reference-guide/latest/postman-quick-reference-guide.pdf
They use Cheerio
2.2.5 How to parse a HTML response to extract a specific value?
Presumed you want to get the _csrf hidden field value for assertions or later use from the response below:
To parse and retrive the value, we will use the cherrio JavaScript library:
responseHTML = cheerio(pm.response.text());
console.log(responseHTML.find('[name="_csrf"]').val());
Cheerio is designed for non-browser use and implements a subset of the jQuery functionality. Read more about it at
https://github.com/cheeriojs/cheerio
responseHTML = cheerio(pm.response.text());
var po= responseHTML.find('[name="_csrf"]').val();
console.log(po);
pm.environment.set("token", po);
/* You need to set the environment in Postman and capture the CSRF token in variable "here po" using a get request. Next in post request the environment variable token can be used */
Just made this JS in post man to parse Without a REGEx. Hope it will help people in the futur
Text to parse : Json : Extract data-id :
{
"code": "OK",
"response": {
"append": {
"html": {
"< .folders": "<a class=\"folder\" href=\"/foobarfoo\" data-id=\"ToExtract\"><div><i class=\"far fa-fw fa-folder\"></i></div><div class=\"folder-name\">blabla</div><div><div class=\"badge\">0</div></div></a>"
}
}
}
}
console.log(responseBody.response);
var jsonData = JSON.parse(responseBody);
var iStart = responseBody.indexOf("response\":")+10;
var scenarioId = responseBody.substr(iStart,10);
var iEnd = scenarioId.indexOf("}");
var scenarioId = scenarioId.substr(0,iEnd);
console.log("scenarioId:" + scenarioId + "iStart: "+ iStart + " scenarioId : " + scenarioId);
pm.environment.set("scenario", scenarioId);

Facing issue while convering json data into xml : mule esb

I tried to convert my json data into xml format. But Only half of data is convert into xml.
My payload is
{"orders":[{"orderName":"Laptop","price":34000,"Date":"2014/01/12","Type":"DELL","stock":52,"code":"152666AS"},
{"orderName":"Chip","price":345,"Date":"2014/02/20","Type":"DELL","stock":50,"code":"152666AW"},
{"orderName":"Laptop1","price":35000,"Date":"2015/02/13","Type":"DELL1","stock":51,"code":"152666AX"}]}
But in output I got only one json item
<?xml version='1.0'?>
<orders>
<orderName>Laptop</orderName>
<price>34000</price>
<Date>2014/01/12</Date>
<Type>DELL</Type>
<stock>52</stock>
<code>152666AW</code>
</orders>
My flow is as follow
<flow name="testFlow">
<http:listener config-ref="HTTP_Quickbook" path="/" doc:name="HTTP"/>
<connector-test:my-processor config-ref="ConnectorTest__Configuration_type_strategy" content="APP" doc:name="ConnectorTest"/>
<json:json-to-xml-transformer mimeType="application/json" doc:name="JSON to XML"/>
<logger message="#[payload]" level="INFO" doc:name="Logger"/>
</flow>
I need whole json string in xml format . What I have to change?
I tested with creating custom transformer.. My custom transformer is as follow
InputStream input = new FileInputStream(new File("test.json"));
OutputStream output = System.out;
XMLInputFactory inputFactory = XMLInputFactory.newInstance();
inputFactory.setProperty(XMLInputFactory.IS_COALESCING, true);
XMLEventReader reader = inputFactory.createXMLEventReader(input);
XMLOutputFactory outputFactory = new JsonXMLOutputFactory();
outputFactory.setProperty(JsonXMLOutputFactory.PROP_PRETTY_PRINT, true);
XMLEventWriter writer = outputFactory.createXMLEventWriter(output);
writer = new XMLMultipleEventWriter(writer, false,"/orders");
writer.add(reader);
reader.close();
writer.close();
Now I got following error
com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character '{' (code 123) in prolog; expected '<'
at [row,col {unknown-source}]: [1,1]
How to solve this?
i can suggest you one thing to make your requirement to work.
if you are want to generate the xml from json.
do the below
add set-payload component to you flow explicitly specify the value as
{"root":#[payload]}
it will work and i could able to generate the xml data out of the sample data you pasted.
please do try and post your status
Your issue comes from the transformation of the array of orders that fails and returns only one entry.
Behind the scene, the json:json-to-xml-transformer uses Staxon for XML to JSON transformation. Here is the doc on array transformation in Staxon: https://github.com/beckchr/staxon/wiki/Mastering-Arrays
We can see in the Mule source that PROP_MULTIPLE_PI is false and PROP_AUTO_ARRAY is not set to true, therefore only one item of the array is considered, the others are dropped.
So the best is for you to write your own transformer either using Staxon too, configured with the array handling settings you want, or using Groovy XML Markup builder to generate the XML in a nice and easy way.
For what you're looking for json
{
"ocs:price": {
"#exponent": "-1",
"#text": "2"
}
}
That's the format to convert for example that from JSON to XML like this :
<ocs:price exponent="-1">2</ocs:price>
In java/groovy script I used something like this to convert it
import net.sf.json.JSON
import net.sf.json.JSONSerializer
import net.sf.json.xml.XMLSerializer
String str = '''{
"ocs:price": {
"#exponent": "-1",
"#text": "2"
}
}'''
JSON json = JSONSerializer.toJSON( str )
XMLSerializer xmlSerializer = new XMLSerializer()
xmlSerializer.setTypeHintsCompatibility( false )
String xml = xmlSerializer.write( json )
System.out.println(xml)

What is the best way to parse GML in VB.Net

I'm looking for the best way to parse GML to return the spatial data. As example here's a GML file:
<?xml version="1.0" encoding="utf-8"?>
<gml:FeatureCollection xmlns:gml="http://www.opengis.net/gml"
xmlns:xlink="http://www.w3.org/1999/xlink"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:onecallgml="http://www.pelicancorp.com/onecallgml"
xsi:schemaLocation="http://www.pelicancorp.com/onecallgml http://www.pelicancorp.com/digsafe/onecallgml.xsd">
<gml:featureMember>
<onecallgml:OneCallReferral gml:id="digsite">
<onecallgml:LocationDetails>
<gml:surfaceProperty>
<gml:Polygon srsName="EPSG:2193">
<gml:exterior>
<gml:LinearRing>
<gml:posList>
1563229.00057526 5179234.72234694 1563576.83066077 5179352.36361939 1563694.22647617 5179123.23451613 1563294.42782719 5179000.13697214 1563229.00057526 5179234.72234694
</gml:posList>
</gml:LinearRing>
</gml:exterior>
</gml:Polygon>
</gml:surfaceProperty>
</onecallgml:LocationDetails>
</onecallgml:OneCallReferral>
</gml:featureMember>
</gml:FeatureCollection>
How do I iterate through each featureMember, and then its polygon(s) and then get the posList coordinates into an array?
When dealing with XML in VB.NET, I recommend using LINQ to XML. You will probably want to extract more information (e.g. something to tie back to the featureMember), but a simple example could be:
' You will need to import the XML namespace
Imports <xmlns:gml = "http://www.opengis.net/gml">
...
Dim xml As XElement = XElement.Parse(myGmlString) ' or some other method
Dim polys = (
From fm In xml...<gml:featureMember>
From poly In fm...<gml:Polygon>
Select New With {
.Name = poly.#srsName,
.Coords = (poly...<gml:posList>.Value.Trim() _
.Split({" "}, StringSplitOptions.RemoveEmptyEntries) _
.Select(Function(x) CDbl(x))).ToArray()
}
).ToList()
This will give you a List of anonymous types with the polygon name and the coordinates as an array of Double.

The ':' character, hexadecimal value 0x3A, cannot be included in a name

I saw this question already, but I didnt see an answer..
So I get this error:
The ':' character, hexadecimal value 0x3A, cannot be included in a name.
On this code:
XDocument XMLFeed = XDocument.Load("http://feeds.foxnews.com/foxnews/most-popular?format=xml");
XNamespace content = "http://purl.org/rss/1.0/modules/content/";
var feeds = from feed in XMLFeed.Descendants("item")
select new
{
Title = feed.Element("title").Value,
Link = feed.Element("link").Value,
pubDate = feed.Element("pubDate").Value,
Description = feed.Element("description").Value,
MediaContent = feed.Element(content + "encoded")
};
foreach (var f in feeds.Reverse())
{
....
}
An item looks like that:
<rss>
<channel>
....items....
<item>
<title>Pentagon confirms plan to create new spy agency</title>
<link>http://feeds.foxnews.com/~r/foxnews/most-popular/~3/lVUZwCdjVsc/</link>
<category>politics</category>
<dc:creator xmlns:dc="http://purl.org/dc/elements/1.1/" />
<pubDate>Tue, 24 Apr 2012 12:44:51 PDT</pubDate>
<guid isPermaLink="false">http://www.foxnews.com/politics/2012/04/24/pentagon-confirms-plan-to-create-new-spy-agency/</guid>
<content:encoded xmlns:content="http://purl.org/rss/1.0/modules/content/"><![CDATA[|http://global.fncstatic.com/static/managed/img/Politics/panetta_hearing_030712.jpg<img src="http://feeds.feedburner.com/~r/foxnews/most-popular/~4/lVUZwCdjVsc" height="1" width="1"/>]]></content:encoded>
<description>The Pentagon confirmed Tuesday that it is carving out a brand new spy agency expected to include several hundred officers focused on intelligence gathering around the world.&amp;#160;</description>
<dc:date xmlns:dc="http://purl.org/dc/elements/1.1/">2012-04-4T19:44:51Z</dc:date>
<feedburner:origLink>http://www.foxnews.com/politics/2012/04/24/pentagon-confirms-plan-to-create-new-spy-agency/</feedburner:origLink>
</item>
....items....
</channel>
</rss>
All I want is to get the "http://global.fncstatic.com/static/managed/img/Politics/panetta_hearing_030712.jpg", and before that check if content:encoded exists..
Thanks.
EDIT:
I've found a sample that I can show and edit the code that tries to handle it..
EDIT2:
I've done it in the ugly way:
text.Replace("content:encoded", "contentt").Replace("xmlns:content=\"http://purl.org/rss/1.0/modules/content/\"","");
and then get the element in the normal way:
MediaContent = feed.Element("contentt").Value
The following code
static void Main(string[] args)
{
var XMLFeed = XDocument.Parse(
#"<rss>
<channel>
....items....
<item>
<title>Pentagon confirms plan to create new spy agency</title>
<link>http://feeds.foxnews.com/~r/foxnews/most-popular/~3/lVUZwCdjVsc/</link>
<category>politics</category>
<dc:creator xmlns:dc='http://purl.org/dc/elements/1.1/' />
<pubDate>Tue, 24 Apr 2012 12:44:51 PDT</pubDate>
<guid isPermaLink='false'>http://www.foxnews.com/politics/2012/04/24/pentagon-confirms-plan-to-create-new-spy-agency/</guid>
<content:encoded xmlns:content='http://purl.org/rss/1.0/modules/content/'><![CDATA[|http://global.fncstatic.com/static/managed/img/Politics/panetta_hearing_030712.jpg<img src='http://feeds.feedburner.com/~r/foxnews/most-popular/~4/lVUZwCdjVsc' height='1' width='1'/>]]></content:encoded>
<description>The Pentagon confirmed Tuesday that it is carving out a brand new spy agency expected to include several hundred officers focused on intelligence gathering around the world.&amp;#160;</description>
<dc:date xmlns:dc='http://purl.org/dc/elements/1.1/'>2012-04-4T19:44:51Z</dc:date>
<!-- <feedburner:origLink>http://www.foxnews.com/politics/2012/04/24/pentagon-confirms-plan-to-create-new-spy-agency/</feedburner:origLink> -->
</item>
....items....
</channel>
</rss>");
XNamespace contentNs = "http://purl.org/rss/1.0/modules/content/";
var feeds = from feed in XMLFeed.Descendants("item")
select new
{
Title = (string)feed.Element("title"),
Link = (string)feed.Element("link"),
pubDate = (string)feed.Element("pubDate"),
Description = (string)feed.Element("description"),
MediaContent = GetMediaContent((string)feed.Element(contentNs + "encoded"))
};
foreach(var item in feeds)
{
Console.WriteLine(item);
}
}
private static string GetMediaContent(string content)
{
int imgStartPos = content.IndexOf("<img");
if(imgStartPos > 0)
{
int startPos = content[0] == '|' ? 1 : 0;
return content.Substring(startPos, imgStartPos - startPos);
}
return string.Empty;
}
results in:
{ Title = Pentagon confirms plan to create new spy agency, Link = http://feeds.f
oxnews.com/~r/foxnews/most-popular/~3/lVUZwCdjVsc/, pubDate = Tue, 24 Apr 2012 1
2:44:51 PDT, Description = The Pentagon confirmed Tuesday that it is carving out
a brand new spy agency expected to include several hundred officers focused on
intelligence gathering around the world. , MediaContent = http://global
.fncstatic.com/static/managed/img/Politics/panetta_hearing_030712.jpg }
Press any key to continue . . .
A few points:
You never want to treat Xml as text - in your case you removed the namespace declaration but actually if the namespace was declared inline (i.e. without binding to the prefix) or a different prefix would be defined your code would not work even though semantically both documents would be equivalent
Unless you know what's inside CDATA and how to treat it you always want to treat is as text. If you know it's something else you can treat it differently after parsing - see my elaborate on CDATA below for more details
To avoid NullReferenceExceptions if the element is missing I used explicit conversion operator (string) instead of invoking .Value
the Xml you posted was not a valid xml - there was missing namespace Uri for feedburner prefix
This is no longer related to the problem but may be helpful for some folks so I am leaving it
As far as the contents of the encode element is considered it is inside CDATA section. What's inside CDATA section is not an Xml but plain text. CDATA is usually used to not have to encode '<', '>', '&' characters (without CDATA they would have to be encoded as < > and & to not break the Xml document itself) but the Xml processor treat characters in the CDATA as if they were encoded (or to be more correct in encodes them). The CDATA is convenient if you want to embed html because textually the embedded content looks like the original yet it won't break your xml if the html is not a well-formed Xml. Since the CDATA content is not an Xml but text it is not possible to treat it as Xml. You will probably need to treat is as text and use for instance regular expressions. If you know it is a valid Xml you can load the contents to an XElement again and process it. In your case you have got mixed content so it is not easy to do unless you use a little dirty hack. Everything would be easy if you have just one top level element instead of mixed content. The hack is to add the element to avoid all the hassle. Inside the foreach look you can do something like this:
var mediaContentXml = XElement.Parse("<content>" + (string)item.MediaContent + "</content>");
Console.WriteLine((string)mediaContentXml.Element("img").Attribute("src"));
Again it's not pretty and it is a hack but it will work if the content of the encoded element is valid Xml. The more correct way of doing this is to us XmlReader with ConformanceLevel set to Fragment and recognize all kinds of nodes appropriately to create a corresponding Linq to Xml node.
You should use XNamespace:
XNamespace content = "...";
// later in your code ...
MediaContent = feed.Element(content + "encoded")
See more details here.
(Of course, you the string to be assigned to content is the same as in xmlns:content="...").