SAP Process Orchestration, SAP Process Integration

SAP PI/PO – Content Conversion plus Java Mapping – ZIDOC

I would like to share one a proof of concept that later one became a project, the solution was to read a text/plain file from FTP/SFTP server and generate the ZIDOC but the text/plain file was not possible to make clear content conversion configuration because it’s not (CSV) file and there was no pattern for reading line by line from the file.

In this case, I developed one generic structure XML to read line by line from the TEXT/PLAIN and the content conversion mechanism generates the generic XML, with the complex java mapping (DOM parsing) the SAP PI system was able to generate the ZIDOC structure dynamically.

Also Read: SAP PO Certification Preparation Guide

I will provide you the explain using the FTP Sender adapter and SFTP Sender Adapter with the Module Standard: AF_Modules/MessageTransformBean to Plain2XMLService.

Integration Diagram:

Inbound interface – FILE to ZIDOC – Sender FTP adapter with content conversion to read the text/plain and generate the generic XML file that will be transformed via java mapping into the ZIDOC with dynamic tags.

Integration Development Steps:

1. Repository:

  • Data Type and Message Type (Content Conversion Generic)
  • Imported Archives – JavaMapping
  • Operation Mapping

2. Directory:

Party / Business Components / SFTP Channels

Process steps – Repository:

  • Data Type and Message Type (Content Conversion Generic):

I created this data type definition to use the content conversion in the Sender FTP Channel to be able to read the input text/plain from the print in a generic format.

Sample of file – text/plain:

I post just part of input text/plain because the file it’s quite big.

K0100
2
K1001/1
28
K1002/1
PQ_VIAL_2ML
K1005/1
MesungzurĂśbertragunginsSAP
K1053/1
PQ-Muster
K1206/1
G-QK-180R
K2001/1
104
K2002/1
Durchmesser_Koerper_Distance_DY
K2003/1
DIM001
...

Content conversion Sender – FTP Channel – Configuration:

Look at the data type details to understand why I proceed with this configuration detail

Configuration details:

  • Root Tag: FileGeneric
  • Namespace: http://namespace
  • RecordSet Structure: Generic,* – Means that it’s 0:unbound the (,*)
  • Generic.fieldNames – Create the child tags Code and Description
  • Generic.fieldFixedLenghts – FixValues 7 for code and 100 for description.
Name Value
Generic.fieldNames  Code,Description
Generic.fieldFixedLenghts  7,100 
ignoreRecordsetName  true 

Sender FTP Adapter Configuration:

The Configuration detail for SFTP Sender adapter:

Module Name Type  Module Key 
AF_Modules/MessageTransformBean Local Enterprise Bean  PlainToXML 

Module parameters:

Module Key Parameter Name   Parameter Value 
PlainToXML  Transform.class com.sap.aii.af.sdk.xi.adapter.Conversion 
PlainToXML  xml.conversionType  SimplePlain2XML 
PlainToXML  xml.documentName  FileGeneric 
PlainToXML  xml.fieldNames  Code 
PlainToXML  xml.fieldNames  Description 
PlainToXML  xml.encoding  UTF-8 
PlainToXML  xml.structureTitle  Generic 

SAP PI Reading the text/plain to XML:

After proceeding with the configuration of content conversion or using the standard module AF_Modules/MessageTransformBean the result of text/plain to XML.

Check how is the generic XML file.

XML File:

<ns:FileGeneric xmlns:ns="http://roche.com/PL/QuickView">
	<Generic>
		<Code>K0100</Code>
		<Description>2</Description>
	</Generic>
	<Generic>
		<Code>K1001/1</Code>
		<Description>28</Description>
	</Generic>
	<Generic>
		<Code>K1002/1</Code>
		<Description>PQ_VIAL_2ML</Description>
	</Generic>
	<Generic>
		<Code>K1005/1</Code>
		<Description>Mesung zur Ăśbertragung ins SAP</Description>
	</Generic>
	<Generic>
		<Code>K1053/1</Code>
		<Description>PQ-Muster</Description>
	</Generic>
	<Generic>
		<Code>K1206/1</Code>
		<Description>G-QK-180R</Description>
	</Generic>
	<Generic>
		<Code>K2001/1</Code>
		<Description>104</Description>
	</Generic>
	<Generic>
		<Code>K2002/1</Code>
		<Description>Durchmesser_Koerper_Distance_DY</Description>
	</Generic>
	<Generic>
		<Code>K2003/1</Code>
		<Description>DIM001</Description>
	</Generic>
	<Generic>
		<Code>K2004/1</Code>
		<Description>0</Description>
	</Generic>
	<Generic>
		<Code>K2009/1</Code>
		<Description>109</Description>
	</Generic>
	<Generic>
		<Code>K2022/1</Code>
		<Description>6</Description>
	</Generic>
	<Generic>
		<Code>K2101/1</Code>
		<Description>16.25</Description>
	</Generic>
	<Generic>
		<Code>K2110/1</Code>
		<Description>16</Description>
	</Generic>
	<Generic>
		<Code>K2111/1</Code>
		<Description>16.5</Description>
	</Generic>
	<Generic>
		<Code>K8500/1</Code>
		<Description>1</Description>
	</Generic>
	<Generic>
		<Code>K8501/1</Code>
		<Description>0</Description>
	</Generic>
	<Generic>
		<Code>K2001/2</Code>
		<Description>105</Description>
	</Generic>
	<Generic>
		<Code>K2002/2</Code>
		<Description>Gesamtlaenge_Optisch_Distance_LC</Description>
	</Generic>
	<Generic>
		<Code>K2003/2</Code>
		<Description>DIM002</Description>
	</Generic>
	<Generic>
		<Code>K2004/2</Code>
		<Description>0</Description>
	</Generic>
</ns:FileGeneric>
  • Repository: Imported Archives – JavaMapping

QuickView Java:

  • Create the IDOC header: EDI_DC40
  • Create the structure Header: ZQ30_APEX_INT_H
  • Create the structure item: ZQ30_APEX_INT_I
  • if (kname.equals(“K2002”)) and if(nextCode.equals(“K2003”))
  • if (kname.equals(“K0001”)) – Check the values
  • Create the XML output.
import java.io.*;

import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import com.sap.aii.mapping.api.*;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.StringWriter;

import java.util.HashMap;
import java.util.Map;
import java.util.TreeMap;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import com.sap.aii.mapping.api.StreamTransformationException;


public class FileQuickView extends AbstractTransformation {

	public AbstractTrace trace;
	org.w3c.dom.Document XML = null;
	
	@Override
	public void transform(TransformationInput arg0, TransformationOutput arg1)
			throws StreamTransformationException {

		this.execute(arg0.getInputPayload().getInputStream(), arg1
				.getOutputPayload().getOutputStream());

	}
	// the method "execute" is called by "transform" and "main"
	public void execute(InputStream in, OutputStream out)
			throws StreamTransformationException {

		final String UTF8 = "UTF-8";

		ByteArrayOutputStream baos = new ByteArrayOutputStream();

		int c = -1;
		String inStr = "";
		String code = "", value = "";

		try {
			while ((c = in.read()) != -1)
				baos.write(c);
			inStr = baos.toString(UTF8);

			InputStream stream = new ByteArrayInputStream(inStr.getBytes(UTF8));

			DocumentBuilderFactory docBuilderFactory = null;
			DocumentBuilder docBuilder = null;
			
			trace = this.getTrace();

			docBuilderFactory = DocumentBuilderFactory.newInstance();
			docBuilderFactory.setNamespaceAware(true);
			docBuilderFactory.setValidating(false);

			docBuilder = docBuilderFactory.newDocumentBuilder();
			XML = docBuilder.parse(stream);
			NodeList nodeListLine = XML.getElementsByTagName("Generic");

			// Cria transformer para processar o XML de saida
			TransformerFactory tFactory = TransformerFactory.newInstance();
			Transformer transformer = tFactory.newTransformer();
			transformer.setOutputProperty(OutputKeys.INDENT, "no");

			// Criando a String a Partir do XML
			StringWriter sw = new StringWriter();
			StreamResult result = new StreamResult(sw);
			DOMSource source = new DOMSource(XML);
			transformer.transform(source, result);

			// Criando a estrutura de saida:
			Element root = XML.createElement("ZQ30APEX");
			Element rootIdoc = XML.createElement("IDOC");
			Element begin = XML.createElement("BEGIN");
			Element edidc40 = XML.createElement("EDI_DC40");
			Element tabnam = XML.createElement("TABNAM");
			Element direct = XML.createElement("DIRECT");
			Element idoctype = XML.createElement("IDOCTYP");
			Element msgtype = XML.createElement("MESTYP");
			Element sndpor = XML.createElement("SNDPOR");
			Element sndprt = XML.createElement("SNDPRT");
			Element sndprn = XML.createElement("SNDPRN");
			Element rcvpor = XML.createElement("RCVPOR");
			Element rcvprn = XML.createElement("RCVPRN");
			Element header = XML.createElement("ZQ30_APEX_INT_H");
			Map<Long, Element> elements = new HashMap<Long, Element>();
			if (nodeListLine != null) {		
				for (int cont = 0; cont < nodeListLine.getLength(); cont++) {			
					if (nodeListLine.item(cont).getNodeType() == Node.ELEMENT_NODE) {	
						Node fstNode = nodeListLine.item(cont);
						code = recuperaNode(fstNode, "Code");
value = recuperaNode(fstNode, "Description");
//						value = value.replace(".", "");
						String[] codeSplit = code.split("/");
						String kname = codeSplit[0];
					    Element genericElement = getGenericElement(getTagName(kname), value);
						if (kname.equals("K1005") || kname.equals("K1053")) {
							genericElement = getGenericElement(getTagName(kname), value);
							header.appendChild(genericElement);
						}	
						if (kname.equals("K2002")) {
							Element item = XML.createElement("ZQ30_APEX_INT_I");
							item.setAttribute("SEGMENT", "1");
							elements.put(new Long(codeSplit[1]), item);
							System.out.println(genericElement.getNodeName());
							System.out.println(genericElement.getTextContent());
							Node nextNode = nodeListLine.item(++cont);
							String nextCode = recuperaNode(nextNode, "Code");
							nextCode = nextCode.substring(0,5);
							item.appendChild(genericElement);
							if(nextCode.equals("K2003")){
								String nextValue = recuperaNode(nextNode, "Description");
								Element genericElementNext = getGenericElement(getTagName(nextCode.substring(0,5)), nextValue);
								item.appendChild(genericElementNext);
							}
						}
						if(kname.equals("K0001")){	
							Element elementWithList = elements.get(new Long(codeSplit[1]));			
							String getTagResult = getTagName(kname);
							getTagResult += elementWithList.getChildNodes().getLength() - 1;
							
							Element newElementToList = getGenericElement(getTagResult, value);
							elementWithList.appendChild(newElementToList);
							
						}
						
					}
				}
			}
// Montando os atributos do xml de saida
			rootIdoc.setAttribute("BEGIN","1");
			edidc40.setAttribute("SEGMENT", "1");
			header.setAttribute("SEGMENT", "1");
			
			// Colocando valores
			tabnam.setTextContent("EDI_DC40");
			direct.setTextContent("2");
			idoctype.setTextContent("ZQ30APEX");
			msgtype.setTextContent("ZQ30APEX");
			sndpor.setTextContent("I30_110");
			sndprt.setTextContent("LS");
			sndprn.setTextContent("I30_110"); 
			rcvpor.setTextContent("SAPI30");
			rcvprn.setTextContent("LS"); 

			// Montanto o raiz Sales ForceInfo
			root.appendChild(rootIdoc);
			rootIdoc.appendChild(edidc40);
			edidc40.appendChild(tabnam);
			edidc40.appendChild(direct);
			edidc40.appendChild(idoctype);
			edidc40.appendChild(msgtype);
			edidc40.appendChild(sndpor);
			edidc40.appendChild(sndprt);
			edidc40.appendChild(sndprn);
			edidc40.appendChild(rcvpor);
			edidc40.appendChild(rcvprn);
			
			rootIdoc.appendChild(header);
			for(Element item : elements.values()) {
				header.appendChild(item);
			}
			// Cria mensagem de SAIDA
			source = new DOMSource(root);
			result = new StreamResult(out);
			transformer.transform(source, result);
		}

		catch (Exception e) {
			throw new SecurityException("XML de origem mal formado: " + e);
		}
	}

	private Element getGenericElement(String kname, String value) {
		Element genericElement;
		genericElement = XML.createElement(kname);
		genericElement.setTextContent(value.trim());
		return genericElement;
	}
	
	private String getTagName(String kcode) {
		if (kcode.equals("K1005"))
			return "CHARG";
		if (kcode.equals("K1053"))
			return "MATNR";
		if (kcode.equals("K2002"))
			return "KURZTEXT";
		if (kcode.equals("K2003"))
			return "VERWMERKM";
		if (kcode.equals("K0001"))
			 return "RESULT_VALUE_";
		return "NameNotDefined";

	}
	
	public static String recuperaNode(Node node, String campo) {
		String result = "";

		Element elmnt = (Element) node;
		NodeList elmntLst = elmnt.getElementsByTagName(campo);
		Element elmnt2 = (Element) elmntLst.item(0);
		if (elmnt2 == null)
			return "";
		NodeList key = elmnt2.getChildNodes();
		if (key.getLength() == 0)
			result = "";
		else
			result = ((Node) key.item(0)).getNodeValue();
		return result;
	}

	// the method "main" is using files for input and output
	public static void main(String[] args) {

		try {
			InputStream in = new FileInputStream(new File(
			"C:\\tmp\\in.xml"));
	OutputStream out = new FileOutputStream(new File(
			"C:\\tmp\\out.xml"));
			FileQuickView2 myMapping = new FileQuickView2();
			myMapping.execute(in, out);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

One of the requirements was to develop a dynamic tag-based on how many values K0001/N and K0004/N the file contents.

These codes contain the most important measure values that the ZIDOC needs, then this can be dynamic (10 or 20 or 32 values or N).

Sample Text/Plain:

K0001/1
16.282448
K0004/1
06/13/2018/12:59:32
K0001/2
34.930577
K0004/2
06/13/2018/12:59:32
K0001/1
16.282375
K0004/1
06/13/2018/13:00:59
K0001/2
34.930524
K0004/2
06/13/2018/13:01:00
K0001/1
16.282353
K0004/1
06/13/2018/13:02:39
K0001/2
34.930376
K0004/2
06/13/2018/13:02:40
K0001/1

  • Repository: Operation Mappings

Just the java mapping:

Testing the XML to ZIDOC via java mapping – DOM parsing:

Below you can see the image of the structure ZQ30_APEX_INT_I with 32 dynamic tags from the measure code values – K0001/N and K0004/N.

Full XML result:

<?xml version="1.0" encoding="UTF-8"?>
<ZQ30APEX>
   <IDOC BEGIN="1">
      <EDI_DC40 SEGMENT="1">
         <TABNAM>EDI_DC40</TABNAM>
         <DIRECT>2</DIRECT>
         <IDOCTYP>ZQ30APEX</IDOCTYP>
         <MESTYP>ZQ30APEX</MESTYP>
         <SNDPOR>I30_110</SNDPOR>
         <SNDPRT>LS</SNDPRT>
         <SNDPRN>I30_110</SNDPRN>
         <RCVPOR>SAPI30</RCVPOR>
         <RCVPRN>LS</RCVPRN>
      </EDI_DC40>
      <ZQ30_APEX_INT_H SEGMENT="1">
         <CHARG>Mesung zur Übertragung ins SAP</CHARG>
         <MATNR>PQ-Muster</MATNR>
         <ZQ30_APEX_INT_I SEGMENT="1">
            <KURZTEXT>Durchmesser_Koerper_Distance_DY</KURZTEXT>
            <VERWMERKM>DIM001</VERWMERKM>
            <RESULT_VALUE_1>16.282448</RESULT_VALUE_1>
            <RESULT_VALUE_2>16.282375</RESULT_VALUE_2>
            <RESULT_VALUE_3>16.282353</RESULT_VALUE_3>
            <RESULT_VALUE_4>16.282261</RESULT_VALUE_4>
            <RESULT_VALUE_5>16.282384</RESULT_VALUE_5>
            <RESULT_VALUE_6>16.282474</RESULT_VALUE_6>
            <RESULT_VALUE_7>16.282307</RESULT_VALUE_7>
            <RESULT_VALUE_8>16.282312</RESULT_VALUE_8>
            <RESULT_VALUE_9>16.28228</RESULT_VALUE_9>
            <RESULT_VALUE_10>16.282251</RESULT_VALUE_10>
            <RESULT_VALUE_11>16.282381</RESULT_VALUE_11>
            <RESULT_VALUE_12>16.282362</RESULT_VALUE_12>
            <RESULT_VALUE_13>16.282408</RESULT_VALUE_13>
            <RESULT_VALUE_14>16.282222</RESULT_VALUE_14>
            <RESULT_VALUE_15>16.282285</RESULT_VALUE_15>
            <RESULT_VALUE_16>16.282365</RESULT_VALUE_16>
            <RESULT_VALUE_17>16.282415</RESULT_VALUE_17>
            <RESULT_VALUE_18>16.282213</RESULT_VALUE_18>
            <RESULT_VALUE_19>16.282285</RESULT_VALUE_19>
            <RESULT_VALUE_20>16.282514</RESULT_VALUE_20>
            <RESULT_VALUE_21>16.282362</RESULT_VALUE_21>
            <RESULT_VALUE_22>16.282324</RESULT_VALUE_22>
            <RESULT_VALUE_23>16.282287</RESULT_VALUE_23>
            <RESULT_VALUE_24>16.282204</RESULT_VALUE_24>
            <RESULT_VALUE_25>16.28233</RESULT_VALUE_25>
            <RESULT_VALUE_26>16.28235</RESULT_VALUE_26>
            <RESULT_VALUE_27>16.282405</RESULT_VALUE_27>
            <RESULT_VALUE_28>16.282343</RESULT_VALUE_28>
            <RESULT_VALUE_29>16.282398</RESULT_VALUE_29>
            <RESULT_VALUE_30>16.28234</RESULT_VALUE_30>
            <RESULT_VALUE_31>16.242278</RESULT_VALUE_31>
            <RESULT_VALUE_32>16.282308</RESULT_VALUE_32>
         </ZQ30_APEX_INT_I>
         <ZQ30_APEX_INT_I SEGMENT="1">
            <KURZTEXT>Gesamtlaenge_Optisch_Distance_LC</KURZTEXT>
            <VERWMERKM>DIM002</VERWMERKM>
            <RESULT_VALUE_1>34.930577</RESULT_VALUE_1>
            <RESULT_VALUE_2>34.930524</RESULT_VALUE_2>
            <RESULT_VALUE_3>34.930376</RESULT_VALUE_3>
            <RESULT_VALUE_4>34.930305</RESULT_VALUE_4>
            <RESULT_VALUE_5>34.930327</RESULT_VALUE_5>
            <RESULT_VALUE_6>34.930208</RESULT_VALUE_6>
            <RESULT_VALUE_7>34.930304</RESULT_VALUE_7>
            <RESULT_VALUE_8>34.930133</RESULT_VALUE_8>
            <RESULT_VALUE_9>34.930442</RESULT_VALUE_9>
            <RESULT_VALUE_10>34.930438</RESULT_VALUE_10>
            <RESULT_VALUE_11>34.930507</RESULT_VALUE_11>
            <RESULT_VALUE_12>34.930299</RESULT_VALUE_12>
            <RESULT_VALUE_13>34.930347</RESULT_VALUE_13>
            <RESULT_VALUE_14>34.930464</RESULT_VALUE_14>
            <RESULT_VALUE_15>34.930346</RESULT_VALUE_15>
            <RESULT_VALUE_16>34.930308</RESULT_VALUE_16>
            <RESULT_VALUE_17>34.930753</RESULT_VALUE_17>
            <RESULT_VALUE_18>34.930172</RESULT_VALUE_18>
            <RESULT_VALUE_19>34.93057</RESULT_VALUE_19>
            <RESULT_VALUE_20>34.929987</RESULT_VALUE_20>
            <RESULT_VALUE_21>34.929909</RESULT_VALUE_21>
            <RESULT_VALUE_22>34.930303</RESULT_VALUE_22>
            <RESULT_VALUE_23>34.930024</RESULT_VALUE_23>
            <RESULT_VALUE_24>34.930196</RESULT_VALUE_24>
            <RESULT_VALUE_25>34.930134</RESULT_VALUE_25>
            <RESULT_VALUE_26>34.93019</RESULT_VALUE_26>
            <RESULT_VALUE_27>34.930148</RESULT_VALUE_27>
            <RESULT_VALUE_28>34.930561</RESULT_VALUE_28>
            <RESULT_VALUE_29>34.93058</RESULT_VALUE_29>
            <RESULT_VALUE_30>34.930503</RESULT_VALUE_30>
            <RESULT_VALUE_31>34.930112</RESULT_VALUE_31>
            <RESULT_VALUE_32>34.929835</RESULT_VALUE_32>
         </ZQ30_APEX_INT_I>
      </ZQ30_APEX_INT_H>
   </IDOC>
</ZQ30APEX>

Leave a Reply

Your email address will not be published. Required fields are marked *