I have a simple flow on AnyPointStudio that consumes a webservices and transforms it to JSON.
Im consuming a webservice that has the periodic table:
http://www.webservicex.net/periodictable.asmx?WSDL
However when using the getAtomicNumber service im getting the data of the xml as a string and the XML to JSON give this result:
{
"GetAtomicNumberResponse" : {
"#xmlns:xsd" : "http://www.w3.org/2001/XMLSchema",
"#xmlns:xsi" : "http://www.w3.org/2001/XMLSchema-instance",
"#xmlns:xmlns" : "http://www.webserviceX.NET",
"GetAtomicNumberResult" : "<NewDataSet>\n <Table>\n <AtomicNumber>49</AtomicNumber>\n <ElementName>Indium</ElementName>\n <Symbol>In</Symbol>\n <AtomicWeight>114.82000000000001</AtomicWeight>\n <BoilingPoint>2300</BoilingPoint>\n <IonisationPotential>5.79</IonisationPotential>\n <EletroNegativity>1.49</EletroNegativity>\n <AtomicRadius>1.5</AtomicRadius>\n <MeltingPoint>430</MeltingPoint>\n <Density>7310</Density>\n </Table>\n</NewDataSet>"
}
}
As you can see it does not parse the GetAtomicNumberResult.
How can I get the entire result parsed as JSON?
The problem you are facing is because the soap response object has an embedded object with the element details. What you would do is, extract the response object and perform xml to json after that. See below:
<flow name="periodic_tableFlow">
<http:listener config-ref="HTTP_Listener_Configuration" path="/table" doc:name="HTTP"/>
<dw:transform-message doc:name="Transform Message">
<dw:set-payload><![CDATA[%dw 1.0
%output application/xml
%namespace ns0 http://www.webserviceX.NET
---
{
ns0#GetAtomicNumber: {
ns0#ElementName: "Indium"
}
}]]></dw:set-payload>
</dw:transform-message>
<logger message="#[message.payloadAs(String)]" level="INFO" doc:name="Logger"/>
<ws:consumer config-ref="Web_Service_Consumer" operation="GetAtomicNumber" doc:name="Web Service Consumer"/>
<dw:transform-message metadata:id="021335ff-1e33-4cdd-be35-49d95f93b8bc" doc:name="Transform Message">
<dw:set-payload><![CDATA[%dw 1.0
%output application/java
%namespace ns0 http://www.webserviceX.NET
---
payload.ns0#GetAtomicNumberResponse.ns0#GetAtomicNumberResult]]></dw:set-payload>
</dw:transform-message>
<json:xml-to-json-transformer doc:name="XML to JSON"/>
<logger message="Before transformer - #[payload]" level="INFO" doc:name="Logger"/>
<echo-component doc:name="Echo"/>
</flow>
</mule>
Related
While using MULE, I convert payload received from HTTP request to json object and mapped it to variables. In case, when I do not pass one of the variable, I get exceptions while evaluating the expression.
<flow name="test">
<http:listener config-ref="HTTP_Listener_Configuration" path="/" doc:name="HTTP"/>
<json:json-to-object-transformer returnClass="java.lang.Object" doc:name="JSON to Object"/>
<set-variable variableName="a" value="#[message.payload.a]"
<set-variable variableName="b" value="#[message.payload.b]"
<logger message="a: #[a]" level="INFO" doc:name="Logger"></logger>
</flow>
When the payload is
{
"a": "val1",
"b": "val2"
}
The applications runs fine.
But if the payload is
{
"b": "val2"
}
I get the following exception:
ERROR [].HTTP_Listener_Configuration.worker.01] org.mule.exception.DefaultMessagingExceptionStrategy:
********************************************************************************
Message : Execution of the expression "a" failed. (org.mule.api.expression.ExpressionRuntimeException).
Payload : {b="val2"}
Payload Type : java.util.LinkedHashMap
Element : /test/processors/10 # test:test.xml:47 (Logger)
Element XML : <logger message="a: #[a]" level="INFO" doc:name="Logger"></logger>
--------------------------------------------------------------------------------
Root Exception stack trace:
[Error: unresolvable property or identifier: a]
[Near : {... a ....}]
Can I check and append the value of variable 'a' as NULL ?
P.S. I would like to use this variable for sending as one of the parameters in payload for some other http request.
using #[flowVars.a] instead of #[a] will result in null instead of a exception.
Something is not right with your implementation. After the http listener, the payload is a raw response(JSON), which in any way cannot be access by #[payload.a] or #[payload['a']]. You should add an json to Object(which converts the json to map). From here you could reference it like this #[payload['a']] or #[payload.a]
Scenario - I have to iterate over this payload, and for those listings with error I would need to increment count. But how to check if error property exists?
{
"jobGuid": "123",
"status": "COMPLETED",
"listings": [
{
"exteralListingId": 7654320
},
{
"exteralListingId": 7654321,
"error": {
"code": "inventory.listings.sellerCreditCardNotfound",
"description": "Seller credit card not found"
}
}
]
}
Option1 - Check using json syntax
Option2 - Iterating in a for-each loop over listings, checked for #[payload.error !=null]. But it gave error - Message payload is of type: LinkedHashMap
You can use jsonPath something like xpath but for JSON
I attached my example with the json provided. As you can see there are #[json:listings] which return array, this array will be iterated by foreach and then validate if contains error tag using #[json:error]. errorCount variable store the number of errors and it will be printed in the console.
<http:listener-config name="HTTP_Listener_Configuration" host="0.0.0.0" port="8081" doc:name="HTTP Listener Configuration"/>
<flow name="demoFlow">
<http:listener config-ref="HTTP_Listener_Configuration" path="/" doc:name="HTTP"/>
<set-payload value="{"jobGuid":"123","status":"COMPLETED","listings":[{"exteralListingId":7654320},{"exteralListingId":7654321,"error":{"code":"inventory.listings.sellerCreditCardNotfound","description":"Seller credit card not found"}},{"exteralListingId":7654321,"error":{"code":"inventory.listings.sellerCreditCardNotfound","description":"Seller credit card not found"}},{"exteralListingId":7654321,"error":{"code":"inventory.listings.sellerCreditCardNotfound","description":"Seller credit card not found"}}]}" doc:name="Set Payload"/>
<expression-transformer expression="#[json:listings]" doc:name="Expression"/>
<set-variable variableName="errorCount" value="#[0]" doc:name="Variable"/>
<foreach collection="#[message.payload]" doc:name="For Each">
<expression-filter expression="#[json:error]" doc:name="Expression"/>
<set-variable variableName="errorCount" value="#[flowVars.errorCount + 1 ]" doc:name="Variable"/>
<logger message="counter: #[errorCount]" level="INFO" doc:name="Logger"/>
</foreach>
</flow>
For more information check the official documentation at mule .
http://www.mulesoft.org/documentation/display/current/JSON+Module+Reference
Mule ESB
I am trying to convert a Hello World program into a program that will accept a JSON object, examine the data, and route the execution accordingly.
Below is a copy of my flow:
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns:json="http://www.mulesoft.org/schema/mule/json" version="EE-3.5.0" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:core="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation" xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns:spring="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd">
<json:object-to-json-transformer name="Object_to_JSON" doc:name="Object to JSON"/>
<flow doc:name="HelloWorldFlow1" name="HelloWorldFlow1">
<http:inbound-endpoint doc:description="This endpoint receives an HTTP message." doc:name="HTTP" exchange-pattern="request-response" host="localhost" port="8081" contentType="application/json"/>
<echo-component doc:name="Echo"/>
</flow>
</mule>
I see the object-to-json in the code, which I believe is configured from the
HTTP Node->HTTP Settings->content type, which is set to
application/json
So I assume that the incoming object is in JSON format once within the flow, with no fursther Object to JSON nodes required.
I am sending the following string to the flow:
{ "uid" : "ABCxxx" }
What I am trying to achieve to get a node that will examine and verify that the first three letters of the uid is "ABC", and if so send it down one path, but if the first three characters of the uid does not equal "ABC", to go down another path., sort of like a IF statement with a true and a false configtion,
The following is a Pseudo code example
IF uid[3] == "ABC"
GOTO Database Connector
else
GOTO JSON-TO-OBJECT Transformer
My question is: Whhich node should I use to perform this, should I use an Expression filter or other
... and how do I write that in JSAONPath (or other)
(Does Mule ESB perform this sort of thing?)
Th json evaluator that executes JsonPath in mule is deprecated in favor of MEL. The preferred approach now is to convert json to an object and query the object instead. One of the simplest approaches is to convert the json to the map and use MEL to query the map. Like so:
<json:json-to-object-transformer returnClass="java.util.HashMap" />
<choice>
<when expression="#[payload.uid == 'ABC']">
</when>
</choice>
More info here: http://www.mulesoft.org/documentation/display/current/Mule+Expression+Language+Tips
You can also <json:json-to-object-transformer returnClass="java.lang.Object" /> alternatively
and then
<choice>
<when expression="#[message.payload.uid == 'ABC']">
I have the following flow:
<?xml version="1.0" encoding="UTF-8"?>
<mule xmlns:json="http://www.mulesoft.org/schema/mule/json" xmlns:db="http://www.mulesoft.org/schema/mule/db" xmlns:tracking="http://www.mulesoft.org/schema/mule/ee/tracking" xmlns:http="http://www.mulesoft.org/schema/mule/http" xmlns="http://www.mulesoft.org/schema/mule/core" xmlns:doc="http://www.mulesoft.org/schema/mule/documentation"
xmlns:spring="http://www.springframework.org/schema/beans" version="EE-3.5.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-current.xsd
http://www.mulesoft.org/schema/mule/core http://www.mulesoft.org/schema/mule/core/current/mule.xsd
http://www.mulesoft.org/schema/mule/db http://www.mulesoft.org/schema/mule/db/current/mule-db.xsd
http://www.mulesoft.org/schema/mule/http http://www.mulesoft.org/schema/mule/http/current/mule-http.xsd
http://www.mulesoft.org/schema/mule/ee/tracking http://www.mulesoft.org/schema/mule/ee/tracking/current/mule-tracking-ee.xsd
http://www.mulesoft.org/schema/mule/json http://www.mulesoft.org/schema/mule/json/current/mule-json.xsd">
<db:generic-config name="Generic_Database_Configuration" url="jdbc:db2://localhost:50000/TEST:user=instuid;password=instpw;" driverClassName="com.ibm.db2.jcc.DB2Driver" doc:name="Generic Database Configuration"/>
<flow name="test2Flow1" doc:name="test2Flow1">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" doc:name="HTTP"/>
<db:select config-ref="Generic_Database_Configuration" doc:name="Database" doc:description="test">
<db:parameterized-query><![CDATA[SELECT SUM(BAL) FROM xxxx.ACCT]]></db:parameterized-query>
</db:select>
<json:object-to-json-transformer doc:name="Object to JSON"/>
</flow>
</mule>
The flow words well, and as you can see the JSON object converts the Database object to the following:
(A JSON arry with a single object):
[{"1":444}]
Notice that the 444 is a numeric value (it does not have quotes around it).
What I want to do
Create a simple JSON structure without an array
Change the 444 from a numeric value to a string value
Make it look something like: (placing the 444 into another structure)
{ "Total" : "444", "Date" : "14/07/14" }
I know that to get the system date, I perform the following:
#[server.dateTime.format('dd/MM/yy')]
... and I know to get the 444 value from the original string, I performed the following:
$..1
But I do not know what to do next.
Now that I used a JSON object to view the results of the database connector, what object do I do next to create my new structure.
Do I use another JSON object, how would I structure the expression ?
To write all numbers as strings you can set it on the Jackson object mapper and reference that custom object mapper from your transformer:
<spring:beans>
<spring:bean id="jacksonObjectMapper" class="org.codehaus.jackson.map.ObjectMapper" />
<spring:bean
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<spring:property name="targetObject" ref="jacksonObjectMapper" />
<spring:property name="targetMethod" value="configure" />
<spring:property name="arguments">
<spring:list>
<spring:value>WRITE_NUMBERS_AS_STRINGS</spring:value>
<spring:value>true</spring:value>
</spring:list>
</spring:property>
</spring:bean>
</spring:beans>
<flow name="flow1" doc:name="flow1">
...
<json:object-to-json-transformer mapper-ref="jacksonObjectMapper" />
...
</flow>
However this will dot it for all number fields. You might have to write a custom serializer for specific behaviour.
As for unwrapping the array. Not sure you can do this via the object mapper with the jackson version mule uses. But for this case, if your always just getting one result back from the query - you could possibly just unfold the array before the json transformer
<set-payload value="#[payload[0]]">
<json:object-to-json-transformer mapper-ref="jacksonObjectMapper" />
A simple way of doing is to use Mule's Expression Transformer.
You need to extract the values from your JSON array and store it into some variables like the following :-
<json:json-to-object-transformer returnClass="java.util.List" doc:name="JSON to Object" />
<set-variable variableName="Total" value="#[message.payload[0].1]" doc:name="Variable" />
Now, your variable Total will contain the value 444
Next step is to store your Date into some other variable as follows :-
<set-variable variableName="Date" value="#[server.dateTime.format('dd/MM/yy')]" doc:name="Variable" />
Now, if these 2 steps are done, then you can create your required JSON in a very easy way using Expression Transformer as follows :-
<expression-transformer
expression="#[[
'Total': flowVars['Total'].toString(),
'Date': flowVars['Date']
]]" doc:name="Expression" />
<json:object-to-json-transformer doc:name="Object to JSON" />
This will produce and structure your required JSON :- {"Date":"12/08/15","Total":"444"} in a very easy way
I've been struggling through SO and the Mule forums but can't find a solution that works. The esb takes in a single request containing an id, which creates an XML payload that is composed a list of related ids. This XML payload is then split and sent to a transformer that takes each related id to get some meta data. The results are then supposed to be combined together to make an XML response. The problem I am having is that no matter how I transform the final result set, I always get back the document with an encapsulating string "??sr)java.util.concurrent.CopyOnWriteArrayListx]....""x
Any ideas/comments would be greatly appreciated.
Here is the mule-config I am using:
<http:connector name="HttpConnector" doc:name="HTTP\HTTPS"/>
<expression-transformer name="ExtractId" doc:name="Expression">
<return-argument evaluator="xpath" expression="//string[1]"/>
</expression-transformer>
<flow name="myFlow1" doc:name="myFlow1">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" path="incoming/path/" connector-ref="HttpConnector" doc:name="HTTP"/>
<custom-transformer encoding="UTF-8" class="mycomponents.transformers.SubscriberRequestTransformer" doc:name="Create Hub Id Requests"/>
<mulexml:object-to-xml-transformer doc:name="Object to XML"/>
<splitter evaluator="xpath" expression="//entry/list/string" enableCorrelation="ALWAYS" doc:name="Splitter"/>
<transformer ref="ExtractId" doc:name="Transformer Reference"/>
<logger message="#[payload:]" level="INFO" doc:name="Logger"/>
<custom-transformer encoding="UTF-8" class="mycomponents.transformers.HubQueryProcessor" doc:name="Create Hub Id Requests"/>
<collection-aggregator />
<component class="mycomponents.TopicResponseAggregatorComponent" doc:name="Collect requests" />
<logger message="Aggregated Content (#[groovy:payload.size()] elements): \n#[payload:]" level="INFO" doc:name="Logger"/>
<logger message="#[payload:]" level="INFO" doc:name="Logger"/>
</flow>
I've resolved the issue. Although I don't quite understand why it's different but I'm assuming it has to do with some of the mule "magic". I split up the flow into multiple flows which is what I was eventually going to do anyways. However, by doing this the problem went away.
FYI: here's the updated flow:
<flow name="myFlow1" doc:name="myFlow1">
<http:inbound-endpoint exchange-pattern="request-response" host="localhost" port="8081" path="url/for/service/" connector-ref="HttpConnector" doc:name="HTTP"/>
<flow-ref name="createHubIdRequestsFlow" doc:name="Map To Hub Ids" doc:description="Takes in the situation id passed in by the client. Does the query lookup versus the content mapping tables. creates the correspondng requests for meta data information to the provider(s)"/>
<flow-ref name="getTopicRequests" doc:name="Process Provider Reponses" doc:description="For each JSON response returned from the provider, transform it into an XML representation of that data"/>
<flow-ref name="buildResponseFlow" doc:name="Build final response" doc:description="piece all the messages together into a single object as a string"/>
<response>
<message-properties-transformer>
<add-message-property value="text/xml" key="Content-Type" />
</message-properties-transformer>
</response>
</flow>
By default, the message properties transformer stores new properties in the "invocation" scope. You need to set the content-type on the "outbound" scope so it is used by the inbound HTTP endpoint in its response phase.
Try this:
<response>
<message-properties-transformer scope="outbound">
<add-message-property value="text/xml" key="Content-Type" />
</message-properties-transformer>
</response>