Perl - Convert a nested XML format to Java with recursion - java

I need to convert a nested XML format as below to Java using Perl:
<invoke name="name1" operation="operation1" displayName="Invoke1" id="6">
<input>
<parameter name="Value" variable="Value"/>
<parameter name="ID" variable="ID"/>
</input>
<output>
<parameter name="Return" variable="Return"/>
</output>
</invoke>
<switch name="..." displayName="..." id="13">
<case id="14">
<condition expressionLanguage="..."><![CDATA[(c1)]]></condition>
</case>
<otherwise id="106">
<switch name="..." displayName="..." id="15">
<case id="16">
<condition expressionLanguage="..."><![CDATA[(c2)]]></condition>
<switch name="..." displayName="..." id="19">
<case id="20">
<condition expressionLanguage="..."><![CDATA[(c3) >0)]]></condition>
</case>
<otherwise id="106"> </otherwise>
</switch>
</case>
<otherwise id="107">
<switch name="..." displayName="..." id="33">
<case id="64">
<condition expressionLanguage="..."><![CDATA[(c4)]]></condition>
</case>
<otherwise id="108"> </otherwise>
</switch>
</otherwise>
</switch>
</otherwise>
</switch>
The expected output as the following:
<invoke name="name1" operation="operation1" displayName="Invoke1" id="6">
<input>
<parameter name="Value" variable="Value"/>
<parameter name="ID" variable="ID"/>
</input>
<output>
<parameter name="Return" variable="Return"/>
</output>
</invoke>
if(c1) {
}else{
if(c2) {
if(c3) {
}else{
}
}else{
if(c4) {
}else{
}
}
}
I think that it may be implemented using 4 steps:
Read XML file -> get the first switch1 block -> convert to if--else
Get case1 block and otherwise1 block of switch1 block
Implement recursion from step1 for case1 block and otherwise1 block
Read the rest of XML file and do the same from s1
It's actually difficult for me to do recursion in this case. Can some Perl experts help me here ?

Here's a solution that uses XML::Parser. I've used Style => 'Subs' because the only events I'm interested in are the start and end of case and otherwise elements, and non-blank character data
The array #indent has another element pushed onto it every time we descend into a block, and the last element is popped off when the block ends. The value of the last element in the array is the number of case elements we've seen so far at this level. That allows us to output else if for all occurrences after the first
I've wrapped the whole of the text in parentheses, because your third condition element contains (c3) >0, which doesn't make sense otherwise
I trust that it's clear enough for you to make any adjustments that you may need
use strict;
use warnings 'all';
use XML::Parser;
my $parser = XML::Parser->new(
Style => 'Subs',
Handlers => { Char => \&handle_char },
);
my #indent = (0);
$parser->parse(*DATA);
sub indent {
' ' x $#indent;
}
sub case {
print indent;
print "else " if $indent[-1] > 0;
print "if ( ";
push #indent, 0;
}
sub case_ {
pop #indent;
++$indent[-1];
print indent, "}\n";
}
sub otherwise {
print indent, "else {\n";
push #indent, 0;
}
sub otherwise_ {
pop #indent;
print indent, "}\n";
}
sub handle_char {
my ($expat, $string) = #_;
print $string, " ) {\n" if $string =~ /\S/;
}
__DATA__
<root>
<switch name="..." displayName="..." id="13">
<case id="14">
<condition expressionLanguage="..."><![CDATA[(c1)]]></condition>
</case>
<otherwise id="106">
<switch name="..." displayName="..." id="15">
<case id="16">
<condition expressionLanguage="..."><![CDATA[(c2)]]></condition>
<switch name="..." displayName="..." id="19">
<case id="20">
<condition expressionLanguage="..."><![CDATA[(c3) >0)]]></condition>
</case>
<otherwise id="106">
</otherwise>
</switch>
</case>
<otherwise id="107">
<switch name="..." displayName="..." id="33">
<case id="64">
<condition expressionLanguage="..."><![CDATA[(c4)]]></condition>
</case>
<otherwise id="108">
</otherwise>
</switch>
</otherwise>
</switch>
</otherwise>
</switch>
</root>
output
if ( (c1) ) {
}
else {
if ( (c2) ) {
if ( (c3) >0) ) {
}
else {
}
}
else {
if ( (c4) ) {
}
else {
}
}
}

Related

apache camel simple language test on null exchange in Bluepring XML not working

I'm using Apache Camel 2.17 and using the simple language to catch a null exchange. It is not working and I've tried several formatting approaches, but it doesn't catch a null. I believe it is the format/syntax of how I'm using it. please instruct where I'm going wrong. thank you!
<process id="_process18" ref="csvMarshallerProcessor"/>
<process id="toReOrgCSV" ref="reOrgCSVData"/>
<choice id="_choice13">
<when id="_when13">
<simple>"${body}" == null</simple>
<log id="_log22" message="body is NULL, do not send NULL body!"/>
<stop id="_stop7"/>
</when>
<otherwise id="_otherwise1">
I've tried
<simple>"${body} == null"</simple>
<simple>"${body}" == null</simple>
<simple>${body} == null</simple>
<simple>${body} == 'null'</simple>
I set the exchange to null in a previous process IF the data is filtered out and ineligiable to send out. I'd like to just use Camel Spring XML.
???
Don't set the exchange as null, as that is not valid, set the message body to null or empty string etc.
exchange.getIn().setBody(null);
And then you can use simple to test its null,
${body} == null
Or if you set it as empty text
${body} == ''
thank you Claus!
this is what I ended up with, ..
...
line.append(System.lineSeparator());
if ( lookup ) {
if ( validate(fleetName, line.toString()) ) {
baos.write(line.toString().getBytes());
}
} else {
baos.write(line.toString().getBytes());
}
}
List<String> serviceRecords = new ArrayList<String>(Arrays.asList(baos.toString().split(System.lineSeparator())));
if ( serviceRecords.size() > 1 ) { //has more than a header
byte[] ba = baos.toByteArray();
exchange.getIn().setBody(ba);
} else {
exchange.getIn().setBody(null); //empty message, only header, no data
}
Camel Context
<process id="_process18" ref="csvMarshallerProcessor"/>
<choice id="_choice13">
<when id="_when13">
<simple trim="true">"${body}" == "" || ${body} == null</simple>
<log id="_log22" message="body is NULL, do not send NULL body!"/>
<stop id="_stop7"/>
</when>
<otherwise id="_otherwise1">
<process id="toReOrgCSV" ref="reOrgCSVData"/>
<to id="_to7" uri="{{DDFEndpoint}}?fileName={{APMLoaderPath}}/${header.aircraftMetadata.shipNumber}-${header.enginePosition}_${header.messageDateTime}.csv"/>
<log id="_log23" message="Sending data packet: ${header.aircraftMetadata.outputPath}/${header.aircraftMetadata.shipNumber}-${header.enginePosition}_${header.messageDateTime}.csv"/>
</otherwise>
</choice>
</aggregate>

Flat file parsing : when some fields contain the delimiter

I have a spring-batch application that reads a file with this reader :
<bean id="tradeItemReader" class="org.springframework.batch.item.file.FlatFileItemReader" scope="step">
<property name="resource">
<bean class="org.springframework.core.io.FileSystemResource">
<constructor-arg value="${input.file.path}/#{jobExecutionContext['trades']}" type="java.lang.String"/>
</bean>
</property>
<property name="linesToSkip" value="1" />
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<!-- split it -->
<property name="lineTokenizer">
<bean
class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<beans:property name="strict" value="false" />
<beans:property name="includedFields" value="0,2,3,6" />
<property name="names"
value="field0,field2,field3,field6" />
</bean>
</property>
<property name="fieldSetMapper">
<bean
class="org.springframework.batch.item.file.mapping.BeanWrapperFieldSetMapper">
<property name="prototypeBeanName" value="trade" />
</bean>
</property>
</bean>
</property>
</bean>
The fields are delimited by a comma ,, and here is the catch : some fields look like [LON, TGT] and the line ends up wrongly parsed, because of the comma inside the square brackets.
Example :
Input : Global,,VERIFIED,[LON, TGT],ERerd,3456585,QTR,20190929,20231020
Desired output : Global, VERIFIED, [LON, TGT], QTR
Actual output : Global, VERIFIED, [LON, 3456585
How can I achieve that ? I don't have control over the input file.
EDIT
This is not a duplicate, as the proposed solution would not work : here we don't have a single quote-character, but we have 2 different ones, the opening bracket and the closing bracket.
As explained by Luca Basso Ricci, my input csv is invalid, but I still have to deal with it because I have no control over it.
So I wrote my own delimited line tokenizer, which is just the DelimitedLineTokenizer with a rewritten isDelimiter() method, and replaced it in the conf file :
private boolean isDelimiter(char[] chars, int i, String token, int endIndexLastDelimiter) {
boolean result = false;
int openingBrackets = StringUtils.countOccurrencesOf(new String(Arrays.copyOfRange(chars, 0, i)), "[");
int closingBrackets = StringUtils.countOccurrencesOf(new String(Arrays.copyOfRange(chars, 0, i)), "]");
boolean inBrackets = (openingBrackets - closingBrackets > 0);
if ((i - endIndexLastDelimiter >= this.delimiter.length()) &&
(i >= token.length() - 1)) {
String end = new String(chars, i - token.length() + 1, token.length());
if (token.equals(end)) {
result = !inBrackets;
}
}
return result;
}

Remove certain recurring elements in xml

I have a xml and want to remove certain elements from the xml document. Initially I wanted to remove the control characters coming in these elements or completely remove the data in these elements.
My sample payload is as below. I wanted to remove resume/content and comments.
<Jobs>
<candidates>
<address2/>
<application>
<comments><BR />Test emp1711 Newberg St. #4 Oregon, CA 97229 444-123-9752 testcandidate#gmail.com<BR />authorName: Test 1
comment content: <b>To: test test (test.test#yahoo.com)</b><br /><br />From: abc Recruitment Team (test#abc.com)<br /><br />Subject: Tester at abc<br /><br /><TEXTFORMAT LEADING="2"><P ALIGN="LEFT"><FONT FACE="Verdana" SIZE="12" COLOR="#353A3F" LETTERSPACING="0" KERNING="0">Hello test,</FONT></P></TEXTFORMAT><TEXTFORMAT LEADING="2"><P ALIGN="LEFT"><FONT FACE="Verdana" SIZE="12" COLOR="#353A3F" LETTERSPACING="0" KERNING="0"></FONT></P></TEXTFORMAT><TEXTFORMAT LEADING="2"><P ALIGN="LEFT"><FONT FACE="Verdana" SIZE="12" COLOR="#353A3F" LETTERSPACING="0" KERNING="0">Thank you for your interest in our abc opening. </FONT></P></comments>
<disposition>tesr Skills match</disposition>
<eId>xyz</eId>
<gender>Male</gender>
<lastUpdatedDate>1340687163</lastUpdatedDate>
<race>Undefined</race>
<resume>
<content>Test<BR />Human Resources Manager / Business Partner <BR /> portland, Oregon; 4454; <BR /> Phone 020 444456;Mobile 088768999;E-mail test#yahoo.com <BR /></content>
<format>Text</format>
</resume>
<sentDate>1789993473</sentDate>
<source>Linkedin</source>
<sourceType>Import</sourceType>
<veteranStatus>Undefined</veteranStatus>
<workflowState>Offer Accepted</workflowState>
<city>portland</city>
<companyName/>
<country>US</country>
<eId>xyz</eId>
</application>
</candidates>
<candidates>
<address2/>
<application>
<comments><BR />Test emp2711 Newberg St. #4 Oregon, CA 97229 444-123-9752 testcandidate2#gmail.com<BR />authorName: Test 2
</comments>
<disposition>Skills match</disposition>
<eId>xyz</eId>
<gender>female</gender>
<lastUpdatedDate>1340687163</lastUpdatedDate>
<race>Undefined</race>
<resume>
<content>Test<BR />Program Manager / Business Partner <BR /> portland, Oregon; 4454; <BR /> Phone 020 444456;Mobile 088768999;E-mail test#yahoo.com <BR /></content>
<format>Text</format>
</resume>
<sentDate>178444473</sentDate>
<source>Linkedin</source>
<sourceType>Import</sourceType>
<veteranStatus>Undefined</veteranStatus>
<workflowState>Offer Accepted</workflowState>
<city>portland</city>
<companyName/>
<country>US</country>
<eId>xyzabc</eId>`enter code here`
</application>
</candidates>
</Jobs>
Build the XML into a Document using DocumentBuilderFactory and pass it to a function like below: (nodeName would be "comments" etc.)
removeAllNodes(Document doc, String nodeName){
DocumentTraversal t = (DocumentTraversal) doc;
Node root = doc.getDocumentElement();
NodeIterator iterator =
t.createNodeIterator(root, NodeFilter.SHOW_ELEMENT, null, true);
for (Node n = iterator.nextNode(); n != null; n = iterator.nextNode()) {
Element e = (Element) n;
if (nodeName.equals(e.getTagName()))
root.removeChild(e);
}
}

SpringBatch Reading and Writing in chunks

I have a program that contains some for-loops. The idea of the program is to log into a website using multiple accounts and retrieve a list (each login brings a different list). So the way I have it setup is with an enhanced for loop:
loginsList.put( "firstUsername", "firstPassword" );
loginsList.put( "secondUsername", "secondPassword" );
loginsList.put( "thirdUsername", "thirdPassword" );
loginsList.put( "fourthUsername", "fourthPassword" );
loginsList.put( "fifthUsername", "fifthPassword" );
for ( Entry<String, String> nextLogin : logins.entrySet() ) {
String nextUser = nextLogin.getKey();
String nextPass = nextLogin.getValue();
Response authenticateUserResponse = Jsoup.connect( WEBSITE_I_NEED_TO_LOGIN_TO )
.data( "username", nextUser )
.data( "password", nextPass )
.execute();
Basically here is what i want the flow to be:
read()--> obtain list----> send list to write() method to write it to the database--> loop back around and get the next login-->read()--> obtain list-->send it to the write()....etc..
however the issue I'm having is that my loop runs in the read method and does not go to the write method until all the lists have been traversed in all the accounts. Essentially the write is only being called once at the end, so what I have right now is something like this(this is the flawed design):
read()--->obtain list-->next account--->obtain list---next account--->obtain list--->write()
How can I organize the chunk processing in Spring to write after I read a chunk only?
for ( Entry<String, String> nextLogin : logins.entrySet() ) {
String nextUser = nextLogin.getKey();
String nextPass = nextLogin.getValue();
//do something
......
//call write function
writeValues(x, y, z);
}
Is this all you want?
Otherwise it seems like a traditional SpringBatch: Read > Process > Proceeed case.
You will have your reader = gets a record
Procesor > saves a record
Spring batch moves you to next record if the was no error.
<step id="processUpdates">
<tasklet task-executor="batchThreadPoolTaskExecutor" throttle-limit="${batch.cviscoreupdate.threadcount}">
<chunk reader="Reader" processor="ItemProcessor" writer="ItemWriter" commit-interval="${batch.commit.interval}" skip-limit="${batch.skip.limit}" >
<skippable-exception-classes>
<include class="batch.support.SkipRecordException" />
</skippable-exception-classes>
</chunk>
</tasklet>
<next on="FAILED" to="errorExit"/>
<next on="*" to="moveFilesFromWorkToDone" />
<listeners>
<listener ref="UpdateSkipListener"/>
</listeners>
</step>
<bean id="CVIScoreUpdateItemProcessor" class="com.batch.MyUpdateItemProcessor" scope="step" init-method="init" />

Payload in Java mule application

If I have two separate mule flows that would run synchronously
<flow name="GatherDDICollection" doc:name= "GatherDDICollection" processingStrategy="synchronous" >
<poll doc:name= "Poll" frequency ="5000">
<invoke object-ref="numberInformationCollectionFlow" method="popWaiting" doc:name="PopPending"/>
</poll>
<expression-filter expression="#[payload != null && ((payload instanceof org. mule.transport. NullPayload) == false) && payload.size() > 0 ]" doc:name ="HasRequest"/>
<logger message= "Collection request - #[payload]" level= "INFO" doc:name="Logger" />
</flow >
<flow name= "ProvisionDDI" doc:name ="ProvisionDDI" processingStrategy="synchronous" >
<poll doc:name="Poll" frequency="5000" >
<invoke object-ref="numberInformationCollectionFlow" method="getParentDdis" doc:name= "DataToProvision"/>
</poll>
<expression-filter expression="#[payload != null && ((payload instanceof org. mule.transport. NullPayload) == false) && payload > 0]" doc:name="HasDataToProvision" />
<logger message= "DDI to provision- #[payload]" level="INFO" doc:name= "Logger"/>
</flow >
Would the payload get confused which flow it belongs to?
I ask because I am confuse with the current scenario:
The first flow (GatherDDICollection) will not show "Collection request -.." because method "popWaiting" returns null.
The second flow (ProvisionDDI) should show "DDI to provision .." because te method "getParentDdis" returns a list of objects but it doesn't (I know this because if I remove the expression-filter on the second flow, the logger shows the message . Is this because it is getting confused with the payload from the first flow?

Categories