cancel
Showing results for 
Search instead for 
Did you mean: 

[SAP:CPI]: Issue with Content Modifier while capturing XPath

babruvahana
Contributor
0 Kudos

Hi Experts,

I want to store an XPath in the property using a content modifier for the attached Payload.

I tried the below, but the response looked weird as below.

So, I have changed the Data Type to java.lang.Object . Now, I am able to capture the data in the property.

Not sure, If I am overlooking or missing something.

Any leads/solutions will be really helpful.

Regards,

Pavan

MortenWittrock
Active Contributor

Hi Pavan

What do you intend to do with the XML afterwards? What you're seeing is expected behaviour, I'd say. You are capturing nodes in your property, not text. Casting it to String, as you tried first, only gets you the text content of those nodes.

Regards,

Morten

Accepted Solutions (0)

Answers (4)

Answers (4)

MortenWittrock
Active Contributor

Hi Pavan

I think your case is a good fit with the Filter step.

But if you use data type "org.w3c.dom.Node", you can achieve what you are trying to do here. Create a property with that type using one of your XPath expressions. You can then replace the message body with that property in a Content Modifier later on.

Regards,

Morten

asutoshmaharana2326
Active Participant

Dear Pavan,

I think you can use either of these below Data Types with xpath : "/CustomPayload/TransformedData" and "/CustomPayload/SourceData" and it will give you exact same results. But each Data Type will create differnt kinds of Saxon impl class (Node, NodeList and NodeInfo) objects.

  • java.lang.Object
  • org.w3c.dom.Node
  • org.w3c.dom.NodeList

After the first contentmodifier whenever you want you can just move those objects from properties to body and you are done. Then you can use them for mapping.

Test -

If you want to use them in groovy then you need to use appropriate class to access them (as they are objects of impl classes of NodeList, Node and NodeInfo). Here i am giving an example of if you have defined data type as "org.w3c.dom.NodeList"

import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
import javax.xml.transform.OutputKeys
import javax.xml.transform.Transformer
import javax.xml.transform.TransformerException
import javax.xml.transform.TransformerFactory
import javax.xml.transform.dom.DOMSource
import javax.xml.transform.stream.StreamResult;
import net.sf.saxon.dom.DOMNodeList;
def Message processData(Message message) {
    def properties = message.getProperties();
    value = properties.get("first");
    message.setProperty("firstString", nodeListToString(value))
    return message;
}
private static String nodeListToString(DOMNodeList nodes) throws TransformerException {
    DOMSource source = new DOMSource();
    StringWriter writer = new StringWriter();
    StreamResult result = new StreamResult(writer);
    Transformer transformer = TransformerFactory.newInstance().newTransformer();
    transformer.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "yes");
    for (int i = 0; i < nodes.getLength(); ++i) {
        source.setNode(nodes.item(i));
        transformer.transform(source, result);
    }
    return writer.toString();
}

Results -

Thanks,

Asu

babruvahana
Contributor

Hi mnw,

Thanks for the response.

I would like to store 2 XPaths for now (maybe more in the future) and pass them to the next steps for further processing which involves mapping where the mapping considers one of these properties as input.

Tried to store the data as properties using the below Groovy as well. But, no luck yet.

import com.sap.gateway.ip.core.customdev.util.Message;
import java.util.HashMap;
import java.util.*;

def Message processData(Message message) {
       def xml = message.getBody(String.class)
       def completeXml= new XmlSlurper().parseText(xml)
       
       def sd = completeXml.RetryPayload.'**'.findAll { node-> node.name() == 'SourceData'}*.text()
       def td = completeXml.RetryPayload.'**'.findAll { node-> node.name() == 'TransformedData'}*.text()
       
       message.setProperty("SourceData", sd)
       message.setProperty("TransformedData", td)

       return message
}<br>

Regards,

Pavan

Willem_Pardaens
Product and Topic Expert
Product and Topic Expert

Hi Pavan,

You can use Node or Nodelist as Data Type in your Exchange Property to preserve the XML tree of the TransformedData node. Using the String data type will only give you the values of the underlying nodes, as you experienced.

See here for examples: https://blogs.sap.com/2017/06/01/sap-cloud-platform-integration-content-filter-in-detail/

babruvahana
Contributor
0 Kudos

Hi willem.pardaens,

Thanks for the response.

I would like to store 2 XPaths(maybe more in the future) and pass them to the next steps for further processing which involves mapping where the mapping considers once of these properties as input. So Filtering the step is not ideal for me :(.

Regards,

Pavan

Willem_Pardaens
Product and Topic Expert
Product and Topic Expert

Could you provide us with a sample XML input, as well as intended output? From your description it is difficult to understand what you are trying to do.

babruvahana
Contributor
0 Kudos

InputPayload:

<RetryPayload>
	<SourceData>
		<Z_SDSL_REPLICATE_SALES_ORDER>
			<REFUND_GIFT_CARD/>
			<SALES_ORDER_BILLING_ADDRESS>
				<CUST_USER_NAME/>
				<CARE_OF/>
			</SALES_ORDER_BILLING_ADDRESS>
			<SALES_ORDER_CUSTOMER>
				<CUSTOMERID>1234565432</CUSTOMERID>
				<GUEST>0</GUEST>
				<NAME>fra fra</NAME>
				<FIRST_NAME>fra</FIRST_NAME>
				<LAST_NAME>fra</LAST_NAME>
			</SALES_ORDER_CUSTOMER>
			<SALES_ORDER_HEADER>
				<VKTYP>CC</VKTYP>
				<TXT_PAYMENT_CARD>VISA ************1111</TXT_PAYMENT_CARD>
				<TOTAL_DISCOUNTS>0.0</TOTAL_DISCOUNTS>
				<ABFAPIAOREQUEST>0</ABFAPIAOREQUEST>
				<AB_INVOICE>0</AB_INVOICE>
				<UPDATEFLAG>I</UPDATEFLAG>
				<DELIVERY_COST>0</DELIVERY_COST>
				<PAYMENT_FEE>0.0</PAYMENT_FEE>
				<CURRENCY>GBP</CURRENCY>
				<ORDER_LANGUAGE>en_GB</ORDER_LANGUAGE>
				<ZZCORPORATEBRANDID>0</ZZCORPORATEBRANDID>
				<ZZBILLING_BLOCK>0</ZZBILLING_BLOCK>
				<ZZORDER_VERSION>14</ZZORDER_VERSION>
				<SPLIT_ORDER>1</SPLIT_ORDER>
				<CONS_SHIPPING_FEE_SWITCH>0</CONS_SHIPPING_FEE_SWITCH>
			</SALES_ORDER_HEADER>
			<VOUCHER/>
		</Z_SDSL_REPLICATE_SALES_ORDER>
	</SourceData>
	<TransformedData>
		<ns2:ZLO_REPLICATE_SALES_ORDER_V2G
			xmlns:ns2="urn:sap-com:document:sap:rfc:functions">
			<SALES_ORDER_CUSTOMER>
				<CUSTOMERID>1234565432</CUSTOMERID>
				<NAME>fra fra</NAME>
				<FIRST_NAME>fra</FIRST_NAME>
				<LAST_NAME>fra</LAST_NAME>
				<TITLE/>
				<PERSONAL_LEGAL_NO/>
				<TEL_NO/>
				<GUEST>0</GUEST>
				<CODICE_DESTINARIO/>
				<CERTIFIED_EMAIL/>
			</SALES_ORDER_CUSTOMER>
			<SALES_ORDER_HEADER>
				<RETURN_ORDERID/>
				<COMPENSATION_ID/>
				<DOC_DATE>2023-05-10</DOC_DATE>
				<VKTYP>CC</VKTYP>
				<ZZPAYTYPE/>
				<TXT_PAYMENT_CARD>VISA ************1111</TXT_PAYMENT_CARD>
				<TOTAL_DISCOUNTS>0.0</TOTAL_DISCOUNTS>
				<ABFAPIAOREQUEST>0</ABFAPIAOREQUEST>
				<ABSTAFFCARDNUMBER/>
				<CONSIGNMENT_ID/>
				<PICKUP_POINT_FLAG/>
			</SALES_ORDER_HEADER>
			<VOUCHER/>
		</ns2:ZLO_REPLICATE_SALES_ORDER_V2G>
	</TransformedData>
</RetryPayload>

Output in Property Name (SourceData):

	<SourceData>
		<Z_SDSL_REPLICATE_SALES_ORDER>
			<REFUND_GIFT_CARD/>
			<SALES_ORDER_BILLING_ADDRESS>
				<CUST_USER_NAME/>
				<CARE_OF/>
			</SALES_ORDER_BILLING_ADDRESS>
			<SALES_ORDER_CUSTOMER>
				<CUSTOMERID>1234565432</CUSTOMERID>
				<GUEST>0</GUEST>
				<NAME>fra fra</NAME>
				<FIRST_NAME>fra</FIRST_NAME>
				<LAST_NAME>fra</LAST_NAME>
			</SALES_ORDER_CUSTOMER>
			<SALES_ORDER_HEADER>
				<VKTYP>CC</VKTYP>
				<TXT_PAYMENT_CARD>VISA ************1111</TXT_PAYMENT_CARD>
				<TOTAL_DISCOUNTS>0.0</TOTAL_DISCOUNTS>
				<ABFAPIAOREQUEST>0</ABFAPIAOREQUEST>
				<AB_INVOICE>0</AB_INVOICE>
				<UPDATEFLAG>I</UPDATEFLAG>
				<DELIVERY_COST>0</DELIVERY_COST>
				<PAYMENT_FEE>0.0</PAYMENT_FEE>
				<CURRENCY>GBP</CURRENCY>
				<ORDER_LANGUAGE>en_GB</ORDER_LANGUAGE>
				<ZZCORPORATEBRANDID>0</ZZCORPORATEBRANDID>
				<ZZBILLING_BLOCK>0</ZZBILLING_BLOCK>
				<ZZORDER_VERSION>14</ZZORDER_VERSION>
				<SPLIT_ORDER>1</SPLIT_ORDER>
				<CONS_SHIPPING_FEE_SWITCH>0</CONS_SHIPPING_FEE_SWITCH>
			</SALES_ORDER_HEADER>
			<VOUCHER/>
		</Z_SDSL_REPLICATE_SALES_ORDER>
	</SourceData>

Output in Property Name(TransformedData):

	<TransformedData>
		<ns2:ZLO_REPLICATE_SALES_ORDER_V2G
			xmlns:ns2="urn:sap-com:document:sap:rfc:functions">
			<SALES_ORDER_CUSTOMER>
				<CUSTOMERID>1234565432</CUSTOMERID>
				<NAME>fra fra</NAME>
				<FIRST_NAME>fra</FIRST_NAME>
				<LAST_NAME>fra</LAST_NAME>
				<TITLE/>
				<PERSONAL_LEGAL_NO/>
				<TEL_NO/>
				<GUEST>0</GUEST>
				<CODICE_DESTINARIO/>
				<CERTIFIED_EMAIL/>
			</SALES_ORDER_CUSTOMER>
			<SALES_ORDER_HEADER>
				<RETURN_ORDERID/>
				<COMPENSATION_ID/>
				<DOC_DATE>2023-05-10</DOC_DATE>
				<VKTYP>CC</VKTYP>
				<ZZPAYTYPE/>
				<TXT_PAYMENT_CARD>VISA ************1111</TXT_PAYMENT_CARD>
				<TOTAL_DISCOUNTS>0.0</TOTAL_DISCOUNTS>
				<ABFAPIAOREQUEST>0</ABFAPIAOREQUEST>
				<ABSTAFFCARDNUMBER/>
				<CONSIGNMENT_ID/>
				<PICKUP_POINT_FLAG/>
			</SALES_ORDER_HEADER>
			<VOUCHER/>
		</ns2:ZLO_REPLICATE_SALES_ORDER_V2G>
	</TransformedData>
Willem_Pardaens
Product and Topic Expert
Product and Topic Expert
0 Kudos

Ok, then just refer to my initial comment, and change the data type from "java.lang.String" to "Node" and it should work.

babruvahana
Contributor
0 Kudos

I tried changing the data type from "java.lang.String" to "Node" and from "java.lang.String" to "Nodelist"

Both didn't work, in fact, deployment failed with the below error.

Error Message:

**********************************

[CONTENT][CONTENT_DEPLOY][RuntimeError] : {"message":"EXCEPTION","parameters":["org.apache.camel.FailedToCreateRouteException: Failed to create route Process_1 at: >>> SetProperty[TransformedData, xpath{//TransformedData}] <<< in route: Route(Process_1)[[From[direct:Test_Message_Steps_TimerEventD... because of java.lang.ClassNotFoundException: Nodelist"],"childMessageInstances":[{"message":"CAUSE","parameters":["org.apache.camel.RuntimeCamelException: java.lang.ClassNotFoundException: Nodelist"],"childMessageInstances":[{"message":"CAUSE","parameters":["java.lang.ClassNotFoundException: Nodelist"]}]}]}

**********************************

That blog explains the Value Type of the Filterstep, I am not sure those are supported as Data Type in Content Modifier.

Willem_Pardaens
Product and Topic Expert
Product and Topic Expert
0 Kudos

My apologies, you have to use the fully qualified name which is "org.w3c.dom.Node" as mentioned by Morten below.