Replace element by XPath - xpath

I try to replace some element of my input XML in Citrus Framework.
My Spring context contains:
<citrus:namespace-context>
<citrus:namespace prefix="def" uri="http://sample.com/xmlns/2005"/>
</citrus:namespace-context>
My input file starts with:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header>
<ns0:canonicalMessageHeader xmlns:ns0="http://sample.com/xmlns/2005">
<ns0:headerVersion>1.0</ns0:headerVersion>
<ns0:bodyVersion>1.0</ns0:bodyVersion>
<ns0:trackingInfo>
<ns0:eventHandlerInitInfo>
<ns0:processInfo>
<ns0:adapterTrackingId>214F27DF-E1FB-4E84-9122-390C5876ABD2:1</ns0:adapterTrackingId>
...
My endpoint is configured in that way:
<send endpoint="jms:topic:Order.Request?timeout=10000&connectionFactory=DEVconnectionFactoryFrom">
<message>
<resource file="com/sample/citrus/messages/input/SalesOrderTo.xml"/>
<element value="${track}" path="SOAP-ENV:Envelope/SOAP-ENV:Header/def:canonicalMessageHeader/def:trackingInfo/def:eventHandlerInitInfo/def:processInfo/def:adapterTrackingId"/>
I have the following error:
Can not evaluate xpath expression 'SOAP-ENV:Envelope/SOAP-ENV:Header/def:canonicalMessageHeader/def:trackingInfo/def:eventHandlerInitInfo/def:processInfo/def:adapterTrackingId'
at com/sample/citrus/SalesOrderToIT(sequential:45)
at com/sample/citrus/SalesOrderToIT(send:48-82)
Caused by: javax.xml.xpath.XPathExpressionException: org.apache.xpath.domapi.XPathStylesheetDOM3Exception: Prefix must resolve to a namespace: def
What's the possible cause of this error?
Best Regards

Global namespace declaration support is missing in Citrus when overwriting message elements in a send operation via XPath. This issue has been tracked: https://github.com/christophd/citrus/issues/331
In the meantime you have to use the exact same namespace prefix as in the message template file - in your case ns0:
Also you could throw away XPath overwrite and use the dot notated Node overwrite like this:
<send endpoint="jms:topic:Order.Request?timeout=10000&connectionFactory=DEVconnectionFactoryFrom">
<message>
<resource file="com/sample/citrus/messages/input/SalesOrderTo.xml"/>
<element value="${track}" path="Envelope.Header.canonicalMessageHeader.trackingInfo.eventHandlerInitInfo.processInfo.adapterTrackingId"/>
</message>
</send>
The dot notation is not based on namespaces but uses the local element names for finding the element in the message template. Obviously not as powerful as XPath but it works with current version of the framework.

You defined nso as namespace prefix in XML but then use def on the XPath, should be nso.

Related

getting first node of xml in logicapps

I need to have first node of below xml that is inside of FIToFICstmrCdtTrf. However, xpath returns binary data.
xpath(xml(triggerBody()),'/')
xpath(xml(triggerBody())x,'/FIToFICstmrCdtTrf')
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Document xmlns="urn:iso:std:iso:20022:tech:xsd:pacs.008.001.02">
<FIToFICstmrCdtTrf>
<GrpHdr>
<MsgId>x</MsgId>
<CreDtTm>x</CreDtTm>
<NbOfTxs>2</NbOfTxs>
....
Your XML is bound to the namespace urn:iso:std:iso:20022:tech:xsd:pacs.008.001.02, so unless you register the namespace in order to be able to use a namespace-prefix in your XPath, you need to address the elements by their local-name().
For instance: /*[local-name()="Document"]/*[local-name()="FIToFICstmrCdtTrf"]
and then to select the first child element (GrpHdr), you can append a generic expression selecting any element * and apply a predicate filter to select the first: /*[1]
Putting it all together:
xpath(xml(triggerBody()),
'/*[local-name()="Document"]/*[local-name()="FIToFICstmrCdtTrf"]/*[1]')
After reproducing from my end, I was able to get this working by applying the below expressions in the flow of my logic app:
xpath(xml(variables('XML')),'/*[local-name()="Document"]/*[local-name()="FIToFICstmrCdtTrf"]/*')?[0]
OR
xpath(xml(variables('XML')),'/*[name()="Document"]/*[name()="FIToFICstmrCdtTrf"]/*')?[0]
Below is the flow of my logic app
RESULTS:

Update parameter value in XML format

I have parameters stored in an XML file. Below is a sample of the file.
<?xml version="1.0" encoding="UTF-8"?>
<root>
<terminal id="A">
<terminalCapacity>3</terminalCapacity>
<terminalMembers id="1">
<memberID>0001</memberID>
<memberCapacity>2</memberCapacity>
</terminalMembers>
</terminal>
<terminal id="B">
<terminalCapacity>4</terminalCapacity>
<terminalMembers id="1">
<memberID>0002</memberID>
<memberCapacity>1</memberCapacity>
</terminalMembers>
<terminalMembers id="2">
<memberID>0003</memberID>
<memberCapacity>3</memberCapacity>
</terminalMembers>
</terminal>
</root>
Each terminalID is associated to a type of simpleModule found in my NED file. The idea is to programmatically update these values throughout the simulation run. The current logic revolves around getting the current parameters in XML format and update the memberCapacity field.
From the Omnet cPar and cXMLElement documentation, I tried using the par("moduleParameter").xmlValue()->getXML() function, but this returns the XML as a string. I also tried using the getAttribute() function, but to no success.
Don't do this. par("moduleParameter").xmlValue() will give you the in memory object tree of the XML document, but that is not meant for modification. Your XML file seems to be just a hierarchical structure and modules and their parameters can mirror that exactly. There is absolutely no reason to reinvent the wheel when you can mirror that with INI file parameters.

XSL - Externalizing Xpath queries to a property file

I went through various posts, regarding reading properties from external property files. Looks like there is a function - getProperty, which can read values from a property file, using a key. I am using saxon parser with spring integration. I am trying something like this, as described in the post :-
spring context file:
<int-xml:xslt-transformer id="xsltTransformer" input-channel="bulkStringInboundChannel"
output-channel="toBridgeChannel" result-type="StringResult" **transformer-factory-class="net.sf.saxon.TransformerFactoryImpl"**
xsl-resource="classpath:/META-INF/spring/integration/intake/intake-flow/bulkTransformer.xsl" />
XSL style sheet:
<xsl:stylesheet version="2.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:xs="http://www.w3.org/2001/XMLSchema">
....
<xsl:variable name="props" select="document('prop.xml')" />
<xsl:value-of select="f:getProperty('query1')"/>
....
Prop.xml:
query1 = /Batch/RequestID/text()
Error description:
XPST0003: XPath syntax error at char 23 on line 30 in {f:getProperty('query1')}:
XTSE0650: No template exists named getProperty
I now have two questions- first of all, how do I get rid of these errors?
Second, can I store xPath queries in property files? The post describes a method, to read a property file and use the value pertaining to its key. However, I am thinking that getProperty will just print the query's text equivalent instead of evaluating the query and processing it. Is there a way to achieve this?
Post - How to read a .properties file inside a .xsl file?
I can't help you with the Spring side of the question, but as for the Saxon side, you can call the JDK method System.getProperty() using code like this:
<xsl:value-of select="System:getProperty('user.dir')" xmlns:System="java:java.lang.System"/>
Java extensibility requires Saxon-PE or higher.
If the value of the property that you read is an XPath expression, you can then execute it using the XSLT 3.0 xsl:evaluate instruction - which also requires Saxon-PE or higher.

Correct Xpath -

I have a xml like -
<?xml version="1.0" encoding="UTF-8"?>
<oslc_cm:Collection xmlns:oslc_cm="http://open-services.net/xmlns/cm/1.0/"
oslc_cm:totalCount="7"
xmlns:dc="http://purl.org/dc/terms/"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:rtc_cm="http://jazz.net/xmlns/prod/jazz/rtc/cm/1.0/">
<rtc_cm:Action rdf:resource="https://rtc.gsissc.myatos.net:9443/ccm/oslc/workflows/_ezLt4PJ7EeGRg6GNKwqw9g/actions/com.ibm.team.workitem.defectWorkflow/com.ibm.team.workitem.defectWorkflow.action.resolve">
<dc:identifier>com.ibm.team.workitem.defectWorkflow.action.resolve</dc:identifier>
<rtc_cm:resultState rdf:resource="https://rtc.gsissc.myatos.net:9443/ccm/oslc/workflows/_ezLt4PJ7EeGRg6GNKwqw9g/states/com.ibm.team.workitem.defectWorkflow/3"/>
<dc:title>Resolve</dc:title>
<rtc_cm:iconUrl>https://rtc.gsissc.myatos.net:9443/ccm/service/com.ibm.team.workitem.common.internal.model.IImageContentService/processattachment/_ezLt4PJ7EeGRg6GNKwqw9g/workflow/resolve.gif</rtc_cm:iconUrl>
</rtc_cm:Action>
From it I have to fetch rtc_cm:resultState rdf:resource.
What would be the correct XPath for it. I am using XPathExpression xPathExpressionDescription = xpath.compile("../rtc_cm:resultState/#rdf:resource"); which is giving me null pointer.
Please guide me .
../rtc_cm:resultState/#rdf:resource
That's assuming the context node – the node to which your XPath expression is relative – is a sibling of rtc_cm:resultState.
You might be better with an absolute path, based on the document root. Something like:
/oslc_cm:Collection/rtc_cm:Action/rtc_cm:resultState/#rdf:resource
(and, of course, assuming you are passing an implementation of IXmlNamespaceResolver set up with all the applicable namespaces).
The absolute path would be:
/oslc_cm:Collection/rtc_cm:Action/rtc_cm:resultState/#rdf:resource
If you know there is only one instance of resultState or if you want to query for all instances:
//rtc_cm:resultState/#rdf:resource
Please make sure that your namespace manager has definitions for the namespaces you use in your XPath expression.

How to move property value from Properties file to payload object using spring integration elements

Sample.properties
=================
http.header.amisys.accept.value=arun/vnd.dsths.services-v1+xml
1)Above XSL automatically loaded when my server starts.
2)I have tried <int:enricher> element but it is not helped me.
Sample Code : Below is bit of code I have tried, Can any one suggest me on this.
<int:channel id="PQLegacySecurity-InputChannel" />
<int:chain input-channel="PQLegacySecurity-InputChannel" >
<!-- Split the Search Request Params from Xml -->
<int-xml:xpath-splitter>
<int-xml:xpath-expression expression="//LegacySecurity" namespace map="xmlMessageNamespace" />
</int-xml:xpath-splitter>
<int:enricher >
<int:payload name="testPayload" expression="${http.header.amisys.accept.value}"/>
</int:enricher>
</int:chain>
Actual Payload Object:Below is the xml which does not contain testPayload property.
<?xml version="1.0" encoding="UTF-8"?><LegacySecurity>
<businessArea>%%%%%%</businessArea>
<LegacySystem>%%%%%</LegacySystem>
<LegacyUserID>%%%%%</LegacyUserID>
<LegacyPassword>%%%%%</LegacyPassword>
<OtherLogin/>
<OtherPassword/>
<AddSecurLogin/>
<AddSecurPassword/>
</LegacySecurity>
Expected Payload Object: Below Object contains new element testPayload node which I should able to add
<?xml version="1.0" encoding="UTF-8"?><LegacySecurity>
<businessArea>%%%%%%</businessArea>
<LegacySystem>%%%%%</LegacySystem>
<LegacyUserID>%%%%%</LegacyUserID>
<LegacyPassword>%%%%%</LegacyPassword>
<OtherLogin/>
<OtherPassword/>
**<testPayload>arun/vnd.dsths.services-v1+xml</testPayload>**
<AddSecurLogin/>
<AddSecurPassword/>
</LegacySecurity>
You can use an xslt transformer. Something like the below, though you will need to figure out correct use of the transformer from the spring docs.
Notice you can pass a parameter through to the XSLT
<int-xml:xslt-transformer result-transformer="toDocumentTransformer" result-type="StringResult" xsl-resource="/xslt/addTestPayload.xslt">
<int-xml:xslt-param name="testPayload" value="${http.header.amisys.accept.value}"/>
</int-xml:xslt-transformer>
In the XSLT file, use this to pick up the parameter:
<xsl:param name="testPayload" />
If you have other changes you need to make to the message you can use the same xslt.

Resources