Using Perl to SaveAsXMLData on Excel 2010 Worksheet - excel

I've an Excel 2010 spreadsheet with an XML map defined within it. Using Perl I want to save the worksheet as XML Data. I do not need to export the XML map file. From within Excel I can select "File > Save As > Save as type : XML Data". This is the output I want to create, but from my Perl script.
I can output the worksheet in CSV format using the SaveAs command with enum 6. I can also output the spreadsheet in XML format using SaveAs with enum 46, but this is not what I want. I want just the XML Data..
There appears to be a SaveAsXMLData function but I'm unable to get it working. Any help appreciated.
use strict;
use warnings;
use Win32::OLE qw(in with);
use Win32::OLE::Const 'Microsoft Excel';
use Win32::OLE::Variant;
use Win32::OLE::NLS qw(:LOCALE :DATE);
$Win32::OLE::Warn = 3; # Die on Errors.
my $Excel = Win32::OLE->GetActiveObject('Excel.Application')
|| Win32::OLE->new('Excel.Application', 'Quit');
$Excel->{DisplayAlerts}=0;
my $excel_file = 'c:\\temp\\master.xlsx';
my $csv_file = 'c:\\temp\\master.csv';
my $xml_file = 'c:\\temp\\master.xml';
my $workbook = $Excel->Workbooks->Open($excel_file);
# Alt+F11 in Excel to start VBA and after that F2 to start Object browser.
# 6 is CSV format
# 46 is XML spreadsheet
$workbook->SaveAs( $csv_file, 6 );
# Now just the XML Data
# The map is called MDBAC_Map
my $objMapToExport = $Excel->Workbooks->XmlMaps("MDBAC_Map");
$workbook->SaveAsXMLData( $xml_file, $objMapToExport );
$workbook->Close();
$Excel->Quit();

Fixed this myself (I was 99% there!). Using the macro recorder within Excel confirmed the required function calls as follows:
ChDir "C:\temp"
ActiveWorkbook.SaveAsXMLData Filename:="C:\temp\master.xml", Map:= _
ActiveWorkbook.XmlMaps("MDBAC_Map")
The line of code for exporting the XML map is wrong. Changed the above code as follows and the script works fine:
my $objMapToExport = $workbook->XmlMaps("MDBAC_Map");

Related

Umlauts in Excel data

I am reading Data From Excel and if the text in the cell contains umlauts (äöü) they not be correctly seen by my Perl script. The char is replaced by substitution character.
What do I need to do to correctly read special characters from Excel?
# get reference to Excel, Active Window, Active Sheet
my $excel = Win32::OLE->GetActiveObject('Excel.Application');
my $book = $excel -> ActiveWindow;
my $sheet = $book -> ActiveSheet();
my $text = $sheet->Cells(1, 2)->{Value};
It works for me (Windows 10, Strawberry Perl 5.30) when printing the content to the Windows command prompt window and using STDOUT encoding cp437:
use feature qw(say);
use strict;
use warnings;
use Win32::OLE;
use open ':std', ':encoding(cp437)';
# get reference to Excel, Active Window, Active Sheet
my $excel = Win32::OLE->GetActiveObject('Excel.Application');
my $book = $excel -> ActiveWindow;
my $sheet = $book -> ActiveSheet();
my $text = $sheet->Cells(1, 1)->{Value};
say $text;
Output:
äöü
Edit:
As noted by #ikegami you should determine the console output-code-page programmatically (instead of hardcoding the value cp437 as I did) like this:
use Win32;
my $coe = "cp" . Win32::GetConsoleOutputCP();
binmode STDOUT, "encoding($coe)";
See also this post for more information.

How to space out tall in page setup scaling option in Excel through Perl

Is there a way to fit to 1 page(s) wide only (tall should be blank) in excel through perl, so that we can set the scaling as "Fit All Columns on One Page". Same as below.
Tried "$worksheet1->fit_to_pages( 1, 0 );", but it is changing to 1,1 (1-wide & 1-tall).
See, you didn't provided any code. Therefore nobody could help you. The module works correctly. Demo:
use strict;
use warnings;
use Excel::Writer::XLSX;
my $workbook = Excel::Writer::XLSX->new( 'test.xlsx' );
my $worksheet = $workbook->add_worksheet();
$worksheet->write( 0, 0, "Hi" );
$worksheet->fit_to_pages(1,0);
Now after opening it in my Excel, it shows:
or in the PageSetup
Even if you check the xml in the xlsx (e.g. rename the .xlsx to .zip and unzip) you will find in the sheet.xml the following snipet:
<pageSetup fitToHeight="0" orientation="portrait"/>
as you can see, the content of the generated .xlsx file is correctly set the fitToHeight (aka "tall") to 0.
So, the problem - as I said in the comment - is in your side.

Can't call method "worksheet" on an undefined value

I have a problem parsing excel file in the same perl code:
I get this error:
"Can't call method "worksheet" on an undefined value at
./parse_pathsim_results.pl line 223"
Interestingly in the perl code I have if I parse another file (delay xls) before the intended slope xls file it works.
Here is the code:
use Spreadsheet::ParseExcel::SaveParser;
$input_delay_csv_file = "./presto/prs/c2x_delay.xls";
$input_slope_excel_file = "./presto/prs/c2x_slope.xls";
$slope_parser = Spreadsheet::ParseExcel::SaveParser->new();
$delay_parser = Spreadsheet::ParseExcel::SaveParser->new();
The code works if I use the following two lines, but I don't want to.
$workbook = $delay_parser->Parse("$input_delay_csv_file");
$worksheet = $workbook->worksheet("Sheet1");
This is where it creates a problem if the above two lines are commented.
$new_workbook = $slope_parser->Parse("$input_slope_excel_file");
$worksheet = $new_workbook->worksheet("Sheet1");
Without you giving much information and only snippets of your code it is hard to say.
The great suspect is the file format itself, as the Parse method does not return a $workbook, the parser returned undef
I recently had a problem like this where the excel file was a quite recent format version. (ending on .xlsx and not .xls) Opening the file with MS Excel and saving as an older excel format did the trick.
Your first file looks as if it was a plain CSV file, and therefore had no problem parsing.

perl script to read an xlsx file(which has many sheets) using the sheet name

I am trying to write a perl script which reads an excel file(which has many sheets in it) using the sheet name.
I know how to access a particular sheet of the excel file using the sheet number, but not sure how to read it using sheet name.
Any help provided is highly appreciated.
Below is the code I wrote to access the sheet using sheet number:
my $Sheet_Number = 26;
my $workbook = ReadData("<path to the excel file>");
for (my $i =2; $i<$limit; $i++){
my $cell = "A" . $i;
my $key_1 = $workbook->[$Sheet_Number]{$cell};
}
Thanks
----Edit----
I want to open the particular sheet within the Excel file by using the sheet name. And then read the data from that particular sheet. The name of the sheet will be entered by the user while running the script from the command line arguments.
Below is the code that I am using after getting suggested answers for my earlier question:
my $parser = Spreadsheet::ParseExcel->new();
my $workbook = $parser->parse("$path");
my $worksheet;
if ($worksheet->get_name() eq "$Sheet_Name"){
for (my $i =2; $i<$limit; $i++){
my $cell = $worksheet->get_cell($i,"A");
my $value = $cell->value();
push #array_keys, $value;
}
}
I want to read the values of Column A of the particular sheet and push it into an array.
$Sheet_Name : It is the name of the sheet which is entered by the user as cmd line arg.
$path : It is the complete path to the Excel file
Error Message: Can't call method "get_name" on an undefined value at perl_script.pl (The error points to the line where the if-condition is used.)
Thanks for the help.
-----EDIT----
Anyone, with any leads on this post, please post your answer or suggestions. Appreciate any responses.
Thanks
The get_name() method of the worksheet object, in conjunction with Perl's grep command should get you this:
my ($worksheet) = grep { $_->get_name() eq 'Sheet2' } $workbook->worksheets();
This would be an un-golfed version of the same:
my $worksheet;
foreach $worksheet ($workbook->worksheets()) {
last if $worksheet->get_name() eq 'Sheet2';
}
Assuming there is a match... if not, I guess my un-golfed version would give you the last worksheet if there was no match.
-- Edit --
I made assumptions and -- you certainly do need to first call the method to load the workbook:
use strict;
use Spreadsheet::ParseExcel;
my $parser = Spreadsheet::ParseExcel->new();
my $workbook = $parser->parse('/var/tmp/foo.xls');
Then the code above should work.

How to save Excel file in working directory in Win32::OLE

I am trying to parse an Excel file in perl. After I extract the required info from it, I close the Excel file. At the end I am trying to save a new Excel file with a different name in the same directory. But this Excel is getting stored in 'My Documents' folder.
use Storable ;
use Cwd;
use Win32::OLE ;
use Win32::OLE qw(in with) ;
use Win32::OLE in ;
use Win32::OLE::Const 'Microsoft Excel';
use Excel::Writer::XLSX;
my $Excel = Win32::OLE->new("Excel.Application");
my $excel = $Excel->Workbooks->Add();
my $sheet = $excel->Worksheets(1);
$sheet->Activate();
my $new_file = "Temp_file.xlsm";
my $new_excel = cwd.'\\'.$new_file;
$new_excel =~ s/\//\\/g;
$excel->SaveAs($new_excel);
$Excel->{DisplayAlerts} = 0;
$excel->{Saved} = 1;
$excel->Close;
Here is an update based on your code. First, you are letting Win32::OLE errors to be silently ignored. Instead, set: $Win32::OLE::Warn = 3 so it croaks whenever something goes wrong. Second, the way you try to obtain an Excel.Application instance is not correct. For one thing, if something goes wrong, you will have instances of Excel will remain floating around.
You are also confusing an Excel instance, a workbook, and the sheets it contains. If you did have $Win32::OLE::Warn = 3, you would have received a notification of where things are going wrong.
You should also always have use strict and use warnings in your script. Others will be more inclined to try to help if they know your problem is not caused by some trivial typo.
You also don't need three separate use Win32::OLE statements.
The code below "works". Compare it to yours.
Finally, if you are manipulating your sheet via Win32::OLE, there is no reason to have Excel::Writer::XLSX in your code.
use feature 'say';
use strict;
use warnings;
use File::Spec::Functions qw( rel2abs );
use Win32::OLE qw(in with) ;
use Win32::OLE::Const 'Microsoft Excel';
$Win32::OLE::Warn = 3;
my $excel = eval {
Win32::OLE->GetActiveObject('Excel.Application');
} || Win32::OLE->new('Excel.Application', sub { $_[0]->Quit });
my $wb = $excel->Workbooks->Add;
my $sheet = $wb->Worksheets->Add;
$wb->SaveAs( rel2abs('temp_file.xlsm') );
$excel->{DisplayAlerts} = 0;
$wb->{Saved} = 1;
$wb->Close;

Resources