2

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?

0

2 Answers 2

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>
4
  • 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
    Commented Sep 16, 2015 at 22:34
  • @david ugh. That's seriously unreadable as a comment Commented Sep 16, 2015 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
    Commented Sep 16, 2015 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
    Commented Sep 16, 2015 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
8
  • 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
    Commented Sep 16, 2015 at 21:00
  • May be use Python for this and Perl for other stuff which I am doing?
    – david
    Commented Sep 16, 2015 at 21:08
  • I recommend passing in the filenames as parameters then.
    – Andras P
    Commented Sep 16, 2015 at 21:19
  • See changed code above, use sys.argv[], then you just call the python script with the appropriate xml names.
    – Andras P
    Commented Sep 16, 2015 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
    Commented Sep 16, 2015 at 21:26

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .