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. 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. 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. 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. Commented Sep 16, 2015 at 21:00
  • May be use Python for this and Perl for other stuff which I am doing? Commented Sep 16, 2015 at 21:08
  • I recommend passing in the filenames as parameters then. 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. 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. Commented Sep 16, 2015 at 21:26

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.