Custom JSON output in Apache Camel xmljson - json

Camel Route :
<camelContext xmlns="http://camel.apache.org/schema/spring">
<dataFormats>
<xmljson id="xmljson" />
</dataFormats>
<route id="route1">
<from uri="file:C:/Users/User1/InputXML"/>
<to uri="activemq:queue:MyThread1"/>
</route>
<route id="route2">
<from uri="activemq:queue:MyThread1"/>
<marshal ref="xmljson"/>
<bean ref="com.test.OutputProcessor"/>
</route>
</camelContext>
Input XML :
<?xml version="1.0" encoding="UTF-8"?>
<Message>
<to> Tove</to>
<from>Jani</from>
<heading>Reminder</heading>
<body>Don't forget me this weekend!</body>
</Message>
Actual output :
{"to":" Tove","from":"Jani","heading":"Reminder","body":"Don't forget me this weekend!"}
I want to customize this output. i want to add some mote attributes to the converted json. For Example i want the output json as
{
"inputs":[
{
"inputname":"to",
"inputValue":"Tove"
},
{
"inputname":"from",
"inputValue":"jani"
},
{
"inputname":"heading",
"inputValue":"Reminder"
},
{
"inputname":"body",
"inputValue":"Don't forget me this weekend!"
}
]
}
How this can be achieved ?

I think an AggregationStrategy might help:
1) Fist you add the aggregationStrategy to your route:
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="direct:start"/>
<enrich strategyRef="aggregationStrategy">
<constant>direct:resource</constant>
<to uri="direct:result"/>
</route>
<route>
<from uri="direct:resource"/>
...
</route>
</camelContext>
<bean id="aggregationStrategy" class="com.ExampleAggregationStrategy" />
2) Then create the class that will get the Body of the message and transform it the way you want, and set the body to the Exchange again.
OBS: Here You will need to use a xml API to add the attributes you want to add.
public class ExampleAggregationStrategy implements AggregationStrategy {
public Exchange aggregate(Exchange original, Exchange resource) {
Object originalBody = original.getIn().getBody();
Object resourceResponse = resource.getIn().getBody();
Object mergeResult = ... // combine original body and resource response
if (original.getPattern().isOutCapable()) {
original.getOut().setBody(mergeResult);
} else {
original.getIn().setBody(mergeResult);
}
return original;
}
}
More here.

Is there anything preventing you from using an XSLT component? You can apply that to bring the input XML to a format that directly maps to your desired output JSON format and then push it to xmljson e.g. - (need some clean up to avoid some blank elements)
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="2.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:template match="Message">
<inputs>
<xsl:for-each select="*">
<inputname><xsl:value-of select="name()" /> </inputname>
<inputvalue><xsl:value-of select="." /></inputvalue>
</xsl:for-each>
</inputs>
</xsl:template>
</xsl:stylesheet>

Use the Jackson library. You can programmatically change the output format. Unmarshal is only good for direct mapping and not enrichment. Essentially Unmarshal to xml, add a processor and then create your output Json format.

Related

need to remove <text xmlns="http://ws.apache.org/commons/ns/payload"> from output in using <xsl:output method="text> xslt mediator

I am using xslt mediator in wso2 studio developer.I add below code as local entity (add resource) in wso2 carbon->conf and use it in my xslt mediator as "key". I have list of objects in output in json format. because even I need list of one object so I use this mediator instead of foreach mediator.
<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet exclude-result-prefixes="xs" version="2.0" xmlns:fn="http://www.w3.org/2005/xpath-functions"
xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:a="http://schemas.datacontract.org/2004/07/Asa.Mbdp.Platform.Publish.Tmc.Head.Proxy"
xmlns:s="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:t="http://tempuri.org/">
<xsl:output encoding="UTF-8" indent="yes" media-type="application/json" method="text"/>
<xsl:strip-space elements="*"/>
<xsl:template match="/">
{
"Response":{
"LivePrices":[
<xsl:for-each select="s:Envelope/s:Body/t:LivePricesResponse/t:LivePricesResult/a:Close">
{
"Last":"<xsl:value-of select="a:Last"/>" ,
"Price":"<xsl:value-of select="a:Price"/>",
"Quantity":"<xsl:value-of select="a:Quantity"/>",
"Since":"<xsl:value-of select="a:Since"/>"
}
<xsl:if test="position()!=last()">,</xsl:if>
</xsl:for-each>
]
}
}
</xsl:template>
</xsl:stylesheet>
my response start with text tag that I do not want to have it in my response. how can I remove this text tag.
<text xmlns="http://ws.apache.org/commons/ns/payload">
{
"Response":{
"LivePrices":[
{
"Last":"1880" ,
"Price":"1851",
"Quantity":"0",
"Since":"2018-07-26T00:00:00"
}
]
}
}
</text>
I want this response:
{
"Response":{
"LivePrices":[
{
"Last":"1880" ,
"Price":"1851",
"Quantity":"0",
"Since":"2018-07-26T00:00:00"
}
]
}
}
After using XSLT place the below code in your sequence
<property name="messageType" scope="axis2" type="STRING" value="application/xml"/>
<payloadFactory media-type="json">
<format>$1</format>
<args>
<arg evaluator="xml" expression="$body//*" xmlns:ns="http://org.apache.synapse/xsd"/>
</args>
</payloadFactory>
<respond/>
This should give you the expected output

Camel DataFormat Jackson using blueprint XML DSL throws context exception

No matter where I place the dataformats in XML DSL blueprint, I get this error just starting at different places. if I remove it, it works but of course I can't convert JSON to POJO. ??? any help or tell me what I'm doing wrong, what i'm missing. thanks!
Error
Unable to start blueprint container for bundle passthrumt1.core/1.0.1.SNAPSHOT
Caused by: org.xml.sax.SAXParseException: cvc-complex-type.2.4.a: Invalid content was found starting with element 'endpoint'. One of '{"http://camel.apache.org/schema/blueprint":redeliveryPolicyProfile, "http://camel.apache.org/schema/blueprint":onException, "http://camel.apache.org/schema/blueprint":onCompletion, "http://camel.apache.org/schema/blueprint":intercept, "http://camel.apache.org/schema/blueprint":interceptFrom, "http://camel.apache.org/schema/blueprint":interceptSendToEndpoint, "http://camel.apache.org/schema/blueprint":restConfiguration, "http://camel.apache.org/schema/blueprint":rest, "http://camel.apache.org/schema/blueprint":route}' is expected.
XML DSL
<camelContext
id="com.passthru.coreCamelContext"
trace="true"
xmlns="http://camel.apache.org/schema/blueprint"
allowUseOriginalMessage="false"
streamCache="true"
errorHandlerRef="deadLetterErrorHandler" >
<properties>
<property key="http.proxyHost" value="PITC-Zscaler-Americas.proxy.corporate.com"/>
<property key="http.proxyPort" value="80"/>
</properties>
<streamCaching id="CacheConfig"
spoolUsedHeapMemoryThreshold="70"
anySpoolRules="true"/>
<!-- -->
<dataFormats>
<json id="Json2Pojo" library="Jackson" unmarshalTypeName="com.passthru.core.entities.TokenEntities">
</json>
</dataFormats>
<endpoint id="predixConsumer" uri="direct:preConsumer" />
<endpoint id="predixProducer" uri="direct:preProducer" />
<endpoint id="getToken" uri="direct:getToken" />
<onException>
<exception>com.passthru.dataservice.PDXDataServiceInvalidDataException</exception>
<redeliveryPolicy maximumRedeliveries="3" />
<handled>
<constant>true</constant>
</handled>
<log
message="Invalid Data From Data Service"
loggingLevel="ERROR" />
<setBody>
<simple>${body.toString}</simple>
</setBody>
<to uri="file:{{errorArchive}}" />
</onException>
If I place the dataformats above properties, it complains, I have to remove properties and streamcache statements in order for it to work. but I need the proxy properties. any suggestions??? thanks again
If the
<camelContext
id="com.ge.digital.passthru.coreCamelContext"
trace="true"
xmlns="http://camel.apache.org/schema/blueprint"
allowUseOriginalMessage="false"
streamCache="true"
errorHandlerRef="deadLetterErrorHandler" >
<dataFormats>
<json id="Json2Pojo" library="Jackson" unmarshalTypeName="com.passthru.core.entities.TokenEntities"/>
</dataFormats>
<properties>
<property key="http.proxyHost" value="PITC-Zscaler-Americas-Cincinnati3PR.proxy.corporate.com"/>
<property key="http.proxyPort" value="80"/>
</properties>
i get this
Caused by: org.xml.sax.SAXParseException: cvc-complex-type.2.4.a: Invalid content was found starting with element 'properties'. One of '{"http://camel.apache.org/schema/blueprint":redeliveryPolicyProfile, "http://camel.apache.org/schema/blueprint":onException, "http://camel.apache.org/schema/blueprint":onCompletion, "http://camel.apache.org/schema/blueprint":intercept, "http://camel.apache.org/schema/blueprint":interceptFrom, "http://camel.apache.org/schema/blueprint":interceptSendToEndpoint, "http://camel.apache.org/schema/blueprint":restConfiguration, "http://camel.apache.org/schema/blueprint":rest, "http://camel.apache.org/schema/blueprint":route}' is expected.
what am I missing?
Camel blueprint XML is validated against camel-blueprint.xsd.
You are interested in complex type with name camelContextFactoryBean which contains sequence of available elements with fixed order.
Correct order of camelContext elements defined in this sequence is:
properties
globalOptions
propertyPlaceholder
package
packageScan
contextScan
jmxAgent
streamCaching
export
defaultServiceCallConfiguration
serviceCallConfiguration
defaultHystrixConfiguration
hystrixConfiguration
routeBuilder
routeContextRef
restContextRef
threadPoolProfile
threadPool
endpoint
dataFormats
transformers
validators
redeliveryPolicyProfile
onException
onCompletion
intercept
interceptFrom
interceptSendToEndpoint
restConfiguration
rest
route
To solve your problem move all endpoint declarations right above dataFormats.

How to invoke a REST call (POST with JSON body) from Camel in blueprint

I want to call a POST rest service in camel blueprint. My blueprint xml file is the following:
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xmlns:camel="http://camel.apache.org/schema/blueprint"
xmlns:cxf="http://camel.apache.org/schema/blueprint/cxf"
xmlns:jaxrs="http://cxf.apache.org/jaxrs"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation=" http://www.osgi.org/xmlns/blueprint/v1.0.0 https://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd http://camel.apache.org/schema/blueprint http://camel.apache.org/schema/blueprint/camel-blueprint.xsd http://cxf.apache.org/jaxrs http://cxf.apache.org/schemas/jaxrs.xsd">
<bean class="org.apache.cxf.jaxrs.provider.json.JSONProvider" id="jsonProvider"/>
<cxf:rsClient address="http://jsonplaceholder.typicode.com/posts"
id="rsClient" loggingFeatureEnabled="true">
<cxf:providers>
<ref bean="jsonProvider" component-id="jsonProvider"/>
</cxf:providers>
</cxf:rsClient>
<camelContext id="RESTCamelContext" xmlns="http://camel.apache.org/schema/blueprint">
<route id="RESTRoute">
<from id="_from1" uri="timer:foor?repeatCount=1"/>
<to id="_to1" uri="log:body?level=INFO"/>
<setHeader headerName="Content-Type" id="_setHeader1">
<constant>application/json</constant>
</setHeader>
<setHeader headerName="Exchange.HTTP_METHOD" id="_setHeader2">
<constant>POST</constant>
</setHeader>
<to id="_to2" uri="cxfrs:bean:rsClient"/>
<to id="_to3" uri="log:body?level=INFO"/>
</route>
</camelContext>
but I don't know how to pass the JSON object in the body request.
The easiest way would be to use HTTP4 component:
<setHeader headerName="CamelHttpMethod">
<constant>POST</constant>
</setHeader>
<setBody> make your POJO here </setBody>
<marshal>
<json library="Jackson" />
</marshal>
<to uri="http4://jsonplaceholder.typicode.com/posts"/>

using Nlog and writing to file as json

I think I'm missing something as I can't seem to figure out how to have it write to a log file in json format using NLog setup in configuration file. The straight rolling file works fine, but not the json. The json target only outputs the message (not in json).
<nlog xmlns="http://www.nlog-project.org/schemas/NLog.xsd" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<targets async="true">
<target xsi:type="File" name="rollingFile" fileName="${basedir}/logs/${shortdate}.log" archiveFileName="${basedir}/logs/{shortdate}_Archive{###}.log" archiveAboveSize="1000000" archiveNumbering="Sequence" layout="${longdate} ${uppercase:${level}} ${callsite} ${message}" />
<target xsi:type="File"
name="rollingFileJson"
fileName="${basedir}/logs/${shortdate}.json"
archiveFileName="${basedir}/logs/{shortdate}_Archive{###}.json"
archiveAboveSize="1000000"
archiveNumbering="Sequence"
layout="${json-encode} ${message}">
</target>
</targets>
<rules>
<logger name="*" minlevel="Trace" writeTo="rollingFile" />
<logger name="*" minlevel="Trace" writeTo="rollingFileJson" />
</rules>
</nlog>
As of the release of NLog 4.0.0 it is possible to use a layout that renders events as structured JSON documents.
Example taken from NLog project site:
<target name="jsonFile" xsi:type="File" fileName="${logFileNamePrefix}.json">
<layout xsi:type="JsonLayout">
<attribute name="time" layout="${longdate}" />
<attribute name="level" layout="${level:upperCase=true}"/>
<attribute name="message" layout="${message}" />
</layout>
</target>
Edit:
You can also create it in code.
Here is the link to the specific part of documentation.
And here the copied example:
var jsonLayout = new JsonLayout
{
Attributes =
{
new JsonAttribute("type", "${exception:format=Type}"),
new JsonAttribute("message", "${exception:format=Message}"),
new JsonAttribute("innerException", new JsonLayout
{
Attributes =
{
new JsonAttribute("type", "${exception:format=:innerFormat=Type:MaxInnerExceptionLevel=1:InnerExceptionSeparator=}"),
new JsonAttribute("message", "${exception:format=:innerFormat=Message:MaxInnerExceptionLevel=1:InnerExceptionSeparator=}"),
}
},
//don't escape layout
false)
}
};
For more info read the docs.
As per NLog documentation: json-encode will only escape output of another layout using JSON rules. It will not "convert" the output to JSON. You'll have to do that yourself.
'{ "date":"${longdate}","level":"${level}","message":${message}}'
Take a look at this question for more details.

JSON: How to log a JSON object value from a vxml?

My aim is to log a JSON object value fethed from a jsp file from the vxml. Is there any way to do that. I see that there is a function called JSON.stringify but thats giving me nothing as a log.
Below is my code:
<?xml version="1.0"?>
<vxml version="2.0" xmlns="http://www.w3.org/2001/vxml">
<var name="userId" expr="1" />
<!--form id="get_location"-->
<data name="userData" method="get" src="http://localhost:5000/userLocation.jsp" nameList="userId" />
<property name="inputmodes" value="dtmf"/>
<menu id="menuZero">
<choice dtmf="1" next="#choice1"/>
<choice dtmf="2" next="#choice2"/>
</menu>
<!--/form-->
<form id="choice1">
<block>
<if cond="userData.HttpResponse.do_queryResponse[&apos;return&apos;].errorMsg.result_code != &apos;0&apos;">
<goto next="welcome.vxml"/>
<else/>
<goto next="welcome.vxml"/>
</if>
</block>
</form>
<form id="choice2">
<block>
<log expr="JSON.stringify(userData.HttpResponse)"/>
</block>
</form>
</vxml>
Maybe, VoiceXML is not supported "JSON.stringify".
Try to get "json2.js" and add code.
<script src="json2.js" />
For example,
<?xml version="1.0" encoding="UTF-8"?>
<vxml
version="2.0"
xmlns="http://www.w3.org/2001/vxml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" >
<script src="json2.js" />
<var name="messageObject" expr="{keyA:'valueA',keyB:'valueB',keyC:'valueC'}" />
<form>
<block><prompt>Write Log!</prompt></block>
<block><log expr="JSON.stringify(messageObject)"/></block>
</form>
</vxml>
I tested this code on "Voxeo Prophecy 13".
I tried json2.js as described above and I had the same issue, 'assignment to undeclared variable JSON'. To fix this, I just declared in the same file (json2.js):
var JSON;
Then it worked properly. In the vxml:
<script><![CDATA[
prueba = new Object();
prueba.pepito = 1234;
prueba.OtraPrueba = "lalalapepe";
]]></script>
<log label="IVB_HISTORY">
<value expr="JSON.stringify(prueba)"/>
</log>
It gets logged as followed:
{"pepito":1234,"OtraPrueba":"lalalapepe"}
I'm using Convergy's InVision Studio