1

I have an xml file (client_23.xml) in which I need to add few lines at a particular section basis on what is there in other xml file (abc_lop.xml).

Here is my abc_lop.xml file in which you see I have lot of ClientField lines with name, pptype and dataType in it.

<Hello version="100">
 <DataHolder numberOfFields="67">
  <ClientField name="target" pptype="aligning" dataType="string">
   <Value value="panel_3646"/>
   <Value value="panel_3653"/>
  </ClientField>
  <ClientField name="category_3652_0_count" pptype="symetrical" dataType="double"/>
  <ClientField name="category_3652_2_count" pptype="symetrical" dataType="double"/>
  <ClientField name="category_3646_0_count" pptype="symetrical" dataType="double"/>
  <ClientField name="pme.cdert" pptype="symetrical" dataType="double"/>
  <ClientField name="pme.age" pptype="symetrical" dataType="double"/>
  <ClientField name="category_3648_1_count" pptype="symetrical" dataType="double"/>
  <ClientField name="pme.number" pptype="symetrical" dataType="double"/>
  <ClientField name="pme.gender" pptype="aligning" dataType="string">
   <Value value=""/>
   <Value value="F   "/>
   <Value value="NA"/>
  </ClientField>
  <ClientField name="pme.status" pptype="aligning" dataType="string">
   <Value value=""/>
   <Value value="A"/>
   <Value value="S"/>
   <Value value="NA"/>
  </ClientField>
  <ClientField name="pme.selling_id" pptype="aligning" dataType="string">
   <Value value="c0"/>
   <Value value="c1"/>
   <Value value="NA"/>
  </ClientField>
 </DataHolder>
</Hello>

I need to read this file and extract name from those ClientField lines and if its pptype is not aligning then I need to construct this line for each name: Below is an example for two names, only first value is different and apart from that other two will always be same.

<eval>upsert("category_3652_0_count", 0, $calty_doubles)</eval>
<eval>upsert("category_3652_2_count", 0, $calty_doubles)</eval>

Now if its pptype is aligning, then construct line like this:

<eval>upsert("target", "NA", $calty_strings)</eval>
<eval>upsert("pme.gender", "NA", $calty_strings)</eval>

And all these changes I have to make in client_23.xml file and then make a new file with it so my new file will look like this as an example: I will have a function with name data_values in which I need to add above stuff in the <block> tag as shown below.

    <function>
        <name>data_values</name>
        <variables>
            <variable>
            <name>temp</name>
            <type>double</type>
            </variable>
        </variables>
        <block>
            <eval>temp = 1</eval>
            <eval>upsert("category_3652_0_count", 0, $calty_doubles)</eval>
            <eval>upsert("category_3652_2_count", 0, $calty_doubles)</eval>
            <eval>upsert("target", "NA", $calty_strings)</eval>
            <eval>upsert("pme.gender", "NA", $calty_strings)</eval>             
        </block>
    </function>

This is what I have currently in client_23.xml file so after adding, it will look like above:

    <function>
        <name>data_values</name>
        <variables>
            <variable>
            <name>temp</name>
            <type>double</type>
            </variable>
        </variables>
        <block>
            <eval>temp = 1</eval>
        </block>
    </function>

I have a very simple shell script which terdon helped me earlier in which we are using perl script as shown below. I adding header and footer in the abc_lop.xml file and then storing it in file variable and then using that file value to put at a particular section in client_23.xml file but I am not sure how to do above things.

SCRIPT:-

for word in $client_types
do
    ## Concatenate the header, the contents of the target file and the
    ## footer into the variable $file.
    file=$(printf '%s\n%s\n%s' "$header" "$(cat "$path/${word}_lop.xml")" "$footer")

    ## Edit the target file and print
    perl -0pe "s#<eval>planting_model = 0</eval>#<eval>planting_model = 1</eval> s#<trestra-config>.* </trestra-config>##sm;   s#<function>\s*<name>DUMMY_FUNCTION.+?</function>#$file#sm" client_"$client_id".xml > "$word"_new_file.xml
done

Here client_types will be like this: abc def pqr and $client_id is 23.

Now I need to add above functionality which I am not sure how can I do that easily?

3

Here's the Perl solution that I tried to post on Stack Overflow before you deleted your duplicate question

use strict;
use warnings;

use XML::LibXML;

# Open the main XML file and locate the
# <block> element that we need to insert into
#
my $doc = XML::LibXML->load_xml(
    location => 'client_23.xml',
    no_blanks => 1,
);
my $block = $doc->find('/function/block')->get_node(1);

# Open the secondary XML file and find all the <ClientField> elements
# that contain the data we need to insert
#
my $abc = XML::LibXML->load_xml(location => 'abc_lop.xml');

for my $field ( $abc->find('/Hello/DataHolder/ClientField')->get_nodelist ) {

    my ($name, $pptype) = map $field->getAttribute($_), qw/ name pptype /;

    my $text = $pptype eq 'aligning' ?
        sprintf q{upsert("%s", "NA", $calty_strings)}, $name :
        sprintf q{upsert("%s", 0, $calty_doubles)}, $name;

    $block->appendTextChild('eval' , $text);
}

print $doc->toString(2);

output

<?xml version="1.0"?>
<function>
  <name>data_values</name>
  <variables>
    <variable>
      <name>temp</name>
      <type>double</type>
    </variable>
  </variables>
  <block>
    <eval>temp = 1</eval>
    <eval>upsert("target", "NA", $calty_strings)</eval>
    <eval>upsert("category_3652_0_count", 0, $calty_doubles)</eval>
    <eval>upsert("category_3652_2_count", 0, $calty_doubles)</eval>
    <eval>upsert("category_3646_0_count", 0, $calty_doubles)</eval>
    <eval>upsert("pme.cdert", 0, $calty_doubles)</eval>
    <eval>upsert("pme.age", 0, $calty_doubles)</eval>
    <eval>upsert("category_3648_1_count", 0, $calty_doubles)</eval>
    <eval>upsert("pme.number", 0, $calty_doubles)</eval>
    <eval>upsert("pme.gender", "NA", $calty_strings)</eval>
    <eval>upsert("pme.status", "NA", $calty_strings)</eval>
    <eval>upsert("pme.selling_id", "NA", $calty_strings)</eval>
  </block>
</function>
  • Thanks for your help and your time, do you see we can integrate this in my shell script somehow? I need to make sure this line ## Edit the target file and print perl -0pe "s#<eval>planting_model = 0</eval>#<eval>planting_model = 1</eval> s#<trestra-config>.* </trestra-config>##sm; s#<function>\s*<name>DUMMY_FUNCTION.+?</function>#$file#sm" client_"$client_id".xml > "$word"_new_file.xml should use the file that gets generated from your step. – david Sep 16 '15 at 22:34
  • @david ugh. That's seriously unreadable as a comment – roaima Sep 16 '15 at 22:47
  • okkk.. What I meant was my perl command is working on original client_23.xml file. What might work is, instead of my perl command working on original client_23.xml file, it should work on xml file generated by Borodin step and combine everything in one shell script. – david Sep 16 '15 at 23:07
  • I need to have everything in one shell script and then I will make that shell script in one line with all escaped strings. – david Sep 16 '15 at 23:10
1

Not perl though, I prefer python for quick xml modifications. Eg:

import xml.etree.ElementTree as ET

file1 = sys.argv[1]
file2 = sys.argv[2]

abc = ET.parse(file1).getroot()
xml2 = ET.parse(file2).getroot()

# For ClientField[name] properties
l = []

block = xml2.find('block')

for node in abc.findall("*/ClientField"):
    if node.attrib['pptype'] == 'aligning':
        ET.SubElement(block, 'eval').text = 'upsert("' + node.get('name') + '", "NA", $calty_strings)'
    else:
        ET.SubElement(block, 'eval').text = 'upsert("' + node.get('name') + '", 0, $calty_doubles)'

print(ET.tostring(xml2))

This will give you:

<function>
    <name>data_values</name>
    <variables>
        <variable>
            <name>temp</name>
            <type>double</type>
        </variable>
    </variables>
    <block>
        <eval>temp = 1</eval>
        <eval>upsert("target", "NA", $calty_strings)</eval>
        <eval>upsert("category_3652_0_count", 0, $calty_doubles)</eval>
        <eval>upsert("category_3652_2_count", 0, $calty_doubles)</eval>
        <eval>upsert("category_3646_0_count", 0, $calty_doubles)</eval>
        <eval>upsert("pme.cdert", 0, $calty_doubles)</eval>
        <eval>upsert("pme.age", 0, $calty_doubles)</eval>
        <eval>upsert("category_3648_1_count", 0, $calty_doubles)</eval>
        <eval>upsert("pme.number", 0, $calty_doubles)</eval>
        <eval>upsert("pme.gender", "NA", $calty_strings)</eval>
        <eval>upsert("pme.status", "NA", $calty_strings)</eval>
        <eval>upsert("pme.selling_id", "NA", $calty_strings)</eval>
    </block>
</function>

EDIT: The shell script will look like this:

client_id=23

for word in $client_types
do
    python converter.py $path/${word}_lop.xml client_"$client_id".xml
done
  • thanks for your suggestion. How can I integrate this in my shell script. I need to have everything at one place as you can see in my above shell script. I am doing other stuff as well with same files. – david Sep 16 '15 at 21:00
  • May be use Python for this and Perl for other stuff which I am doing? – david Sep 16 '15 at 21:08
  • I recommend passing in the filenames as parameters then. – Andras P Sep 16 '15 at 21:19
  • See changed code above, use sys.argv[], then you just call the python script with the appropriate xml names. – Andras P Sep 16 '15 at 21:21
  • hmmm, problem is I need to have everything in one shell script and then I need to convert that shell script in one line and put it on some other place and then I am executing it. – david Sep 16 '15 at 21:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.