pavani
pavani

Reputation: 143

Why do I get an error in FOREACH loop using Template?

I need help with this problem, I am struggling from last 15days please help me.I have xml file and I stored in a variable using XML::simple. I processed through template toolkit. I am getting error like this

$var1={
 university=>{
      'name'=>'svu',
       'location'=>'ravru',
        'branch'=>{
                 'electronics'=>{
                            'student'=>[
                                     {
                                    'name'=>'mikky',
                                     'number'=>'12',
                                       'semester'=>{
                                              'Number'=>'1',
                                              'subjects'=>'7',
                                               'rank'=>'1'
                                              }
                                           },
                                   {
                                    'name'=>'vijju',
                                     'number'=>'15',
                                       'semester'=>[
                                            {
                                           'number'=>'1',
                                            'subjects'=>'7',
                                             'rank'=>'10'
                                              },
                                             {
                                            'number'=>'1',
                                            'subjects'=>'7',
                                             'rank'=>'1'
                                              }
                                             ]
                                           },
                                          {
                                    'name'=>'shyam',
                                     'number'=>'16',
                                       'semester'=>[
                                            {
                                           'number'=>'1',
                                            'subjects'=>'7',
                                             'rank'=>'2'
                                              },
                                             {
                                            'number'=>'2',
                                            'subjects'=>'4',
                                             'rank'=>'2'
                                              }
                                             ]
                                           }
                                         }
                                      ]
                                     }
                                   };
           university=>{
                 'name'=>'sku',
                'location'=>'ANTP',
                  'branch'=>{
                        'electronics'=>{
                                  'student'=>[
                                            {
                                          'name'=>'xxx',
                                           'number'=>'12',
                                          'semester'=>{
                                                  'Number'=>'3',
                                                    'subjects'=>'6',
                                                  'rank'=>'20'
                                              }
                                           },
                                   {
                                    'name'=>'xxx',
                                     'number'=>'6',
                                       'semester'=>[
                                            {
                                           'number'=>'1',
                                            'subjects'=>'9',
                                             'rank'=>'12'
                                              },
                                             {
                                            'number'=>'2',
                                            'subjects'=>'4',
                                             'rank'=>'2'
                                              }
                                             ]
                                           }
                                         }
                                      ]
                                     }
                                   };
      'studentaddres'=>{
                 'address'=>[
                            {
                           'name'=>'mikky',
                           'number'=>'12',
                           'adress'=>' badvel,kadapa,a.p,india',
                            },

                           {
                           'name'=>'vijju',
                           'number'=>'15',
                           'adress'=>' raipur,ananthapur,a.p,india',
                            },

                           {
                           'name'=>'shyam',
                           'number'=>'16',
                           'adress'=>' raighad,rajsthan,india',
                            },
                          ]
                       }

(New comment: I have the data like this, If We look into deep I have the same name and number in student element and also in address element, I have one more thing adress in address element. now I need to search student name and number in address element and get the address of that student. Some times I have same names with different numbers so I need to search both number and name for student address.)

and I processed like this

#!/usr/bin/perl
use warnings;
use strict;
use Data::Dumper; 
use XML::Simple;
use XML::Fast;
use Template;

my $xml = new XML::Simple;
my $data = $xml->XMLin("data.xml");
print Dumper($data);
my $template = Template->new();
my $filename = 'output.tex';
$template->process(\*DATA, $data, $filename)
    || die "Template process failed: ", $template->error(), "\n";

system( "pdflatex $filename" );
__DATA__
\documentclass[a4paper,leqno,twoside]{article}
\begin{document}

[% FOREACH st IN university %]
[% st.name %] 
[%st.location%]
studentdata:
[% FOREACH student IN st.branch.electronics.student %]
Component type: [%+ student.name +%][%+ student.number +%]
[% FOREACH semester IN student.semester %]
[%+ semester.number +%]
[%+ semester.subjects +%]
[%+  semester.rank +%]
[% END %]
[% END %]
[% END %]

\end{document}

But I getting Different output rather than what I want. After executing this script I am getting output when I have three or more students in electronics and two or more semesters for each student then only I am getting. otherwise it does print any thing just printing studentdata: three times . for example if I have one student with one semester in university at that situation it doesn't give any thing it printing like this

       studentdata:
           studentdata:
            studentdata:

why for loop is executing like that, I can't understand really what wrong win the for loop but I need to print like this

name:
      svu
location: 
      ravru
student data:
   student name=xxx  number=12
  semester number=1 subjects=7 rank=2
   student name=xxx  number=15
  semester number=1 subjects=7 rank=10
 semester number=2 subjects=4 rank=1
 student name=xxx  number=16
 semester number=1 subjects=7 rank=2
 semester number=2 subjects=4 rank=2

#!/usr/bin/perl
 use warnings;
 use strict;
 use Data::Dumper; 
 use XML::Simple;
 use XML::Fast;
 use Template;

 my $studentaddres;

  my $xml = new XML::Simple;
 my $data = $xml->XMLin("data.xml", ForceArray=>1);
  print Dumper($data);

 my $template = Template->new();
   my $filename = 'output.tex';
   $template->process(\*DATA, $data, $filename)
 || die "Template process failed: ", $template->error(), "\n";

 $studentaddres->{by_student_number} 
 = { map { $_->{number} => $_ } @{ $studentaddres->{address} }};

 system( "pdflatex $filename" );
  __DATA__
\documentclass[a4paper,leqno,twoside]{article}
   \begin{document}

 [% FOREACH st IN university %]
  [% st.name %] 
   [%st.location%]
  studentdata:
  [% FOREACH student IN st.branch.electronics.student %]
    Component type: [%+ student.name +%][%+ student.number +%]

  [% address = studentaddres.by_student_number.item( student.number );
  IF address %]
 [% address.adress %]
 [% END %]
 [% FOREACH semester IN student.semester %]
  [%+ semester.number +%]
   [%+ semester.subjects +%]
   [%+  semester.rank +%]
    [% END %]
   [% END %]
   [% END %]

  \end{document}

Upvotes: 0

Views: 274

Answers (1)

Axeman
Axeman

Reputation: 29844

First of all, your data is not valid perl. Secondly, iterating through a hash gives you a (Java-like) "entry" objects. So 'st' => { key => 'name', value => 'svu' }. Thus st.name is nothing.

This is more like what you want (or what works, anyway ):

name:
      [% university.name %] 
location:
      [% university.location %]
studentdata:
   [%- FOREACH student IN university.branch.electronics.student %]
      student name=[% student.name %] number=[% student.number %]
       [%- FOREACH semester IN student.semester %]
     semester number=[% semester.number %] subjects=[% semester.subjects 
           %] rank=[% semester.rank %]
       [%- END -%]
   [%- END -%]

And I had to change 'semester' on student #12 to be an array and de-capitalize 'number'. This is what you had:

   'semester'=>{
          'Number'=>'1',
          'subjects'=>'7',
           'rank'=>'1'
          }

This is what I changed it to:

   'semester'=>[{
          'number'=>'1',
          'subjects'=>'7',
           'rank'=>'1'
          }]

Without semester as an array, you were just iterating through this type of thing again:

( { key => 'number',   value => '1' }
, { key => 'subjects', value => '7' }
, { key => 'rank',     value => '1' } 
)

And not necessarily (or even likely) in that order.

Using a properly closed $var1 as the data, the code I put above gives me:

   \documentclass[a4paper,leqno,twoside]{article}
   \begin{document}

name:
      svu 
location:
      ravru
studentdata:
      student name=xxx number=12
     semester number=1 subjects=7 rank=1
      student name=xxx number=15
     semester number=1 subjects=7 rank=10
     semester number=1 subjects=7 rank=1
      student name=xxx number=16
     semester number=1 subjects=7 rank=2
     semester number=2 subjects=4 rank=2
  \end{document}

To debug your script, you might want to make the following changes:

my $template = Template->new( { EVAL_PERL => 1 } );

And then, where you want to see the structure that you're addressing, do this:

 [% RAWPERL %]
 use Data::Dumper ();
 print Data::Dumper->Dump( [ $stash->get( 'semester' ) ], [ 'semester' ] ), "\n";
 [% END %]

You'll quickly see when the hash in semester has no number, subjects, or rank fields.


(Answering comment) Well, if you have some students with addresses as some without, you could do the following:

[% IF student.address;
    addr = student.address; %][%-
    -%]Address: [% addr.lines.1 %]
                [% addr.lines.2 %]
                [% addr.city %], [% addr.state %] [% addr.zip %]
[% END %]

Or if you had a hash of addresses, you could do the following.

[% IF address_for_student.exists( student.number );
     address = address_for_student.item( student.number );
     %]
...
[% END %]

(Answering new address part of question)

You can massage your data before presentation. This is what I suggest before passing the data to the presentation layer. (Here $studentaddres is just a reference to the structure in the stash.)

$studentaddres->{by_student_number} 
    = { map { $_->{number} => $_ } @{ $studentaddres->{address} }}
    ;

Then, in the presentation layer you can just do this:

[% address = studentaddres.by_student_number.item( student.number );
   IF address %]
   ...
[% END %]

In general, you should have the links that you need prior to the presentation layer, that makes the presentation layer just a matter of displaying the data.

XML stores your model. Fine. XML does trees just fine, but doesn't do complex graphs too well. So if you have complex relationships (and XML::Simple is not going to know anything about those) you have to use this type of algorithmic model: retrieve → link → display. You have to do this with YAML or JSON or even rows pulled from a database, so the link stage is second-nature to me.

The presentation layer is the wrong layer for searching. It should just handle lists of 0, 1, or many or simple cases like relationship exists-or-not.

Upvotes: 3

Related Questions