NPOI Write Corrupts File - Bare Ampersands - excel

Using NPOI 2.1.3.1, I am trying to read an existing Excel (*.xlsx) workbook, modify it, and then write it back to the original file. After reading various threads (including this one), I still cannot find a solution to the problem I'm having.
When I write the file to disk and then try to open it again in Excel, I get the following error:
We found a problem with some content in (filename. Do you want us to
try to recover as much as we can? If you trust the source of this
workbook, click Yes.
Clicking "Yes" fixes various problems in the Excel file, after which I see the following report of the fixes performed:
Replaced Part: /xl/worksheets/sheet3.xml part with XML error. Illegal
name character. Line 3, column 3891168.
Replaced Part: /xl/worksheets/sheet19.xml part with XML error. Illegal name
character. Line 1, column 699903.
Removed Records: Formula from /xl/calcChain.xml part (Calculation properties)
I unzipped the *.xlsx file and found the sheets mentioned and discovered that the character it was referring to is a bare ampersand (&) that was not written as "&" in the XML. The original does use "&", but the file NPOI wrote does not. I have no idea what the issue is with the formula (third issue).
Here is a complete program that reproduces this issue every single time with the workbook I'm using, with the file name removed:
using System.IO;
using NPOI.XSSF.UserModel;
namespace NpoiTest
{
public sealed class NpoiTest
{
public static void Main(string[] args)
{
XSSFWorkbook workbook;
using (FileStream file = new FileStream(#"C:\Path\To\File.xlsx", FileMode.Open, FileAccess.Read))
{
workbook = new XSSFWorkbook(file);
}
using (FileStream file = new FileStream(#"C:\Path\To\File.xlsx", FileMode.Create, FileAccess.Write, FileShare.ReadWrite))
{
workbook.Write(file);
}
}
}
}
As a test, I wrote pretty much the same program using Apache POI, to see if it was just a universal problem with my workbook, and the result was that POI didn't have any problems.
Here is the complete program:
package poitest;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
public class PoiTest
{
public static void main(String[] args)
{
XSSFWorkbook workbook;
try (FileInputStream file = new FileInputStream(new File("C:\\Path\\To\\File.xlsx")))
{
workbook = new XSSFWorkbook(file);
}
catch (IOException e)
{
System.out.println(e.getMessage());
return;
}
try (FileOutputStream out = new FileOutputStream(new File("C:\\Path\\To\\File.xlsx")))
{
workbook.write(out);
}
catch (IOException e)
{
System.out.println(e.getMessage());
}
}
}
So the question is why is NPOI leaving the bare ampersands? Is this just a bug in NPOI?

Related

Is disposing SXSSFWorkbook necessary when used in try with resource

Below is the sample code snippet to create SXSSFWorkbook:
try(SXSSFWorkbook wb = new SXSSFWorkbook()) {
//...
} finally {
wb.dispose(); //wb not accessible over here, so can't use try with resource
}
Here problem is that if I use try with resource then can't dispose() SXSSFWorkbook in finally, as variable wb won't be accessible in finally block.
I wanted know that is disposing of workbook necessary to delete temporary files or since SXSSFWorkbook is AutoCloseable, try with resource will take care of it.
Not sure whether someone of the apache poi programmers will answering this. But apache poi is open source. So every programmer can answering this itself by looking at the code.
State May 2018, apache poiversion 3.17.
SXSSFWorkbook.java:
public class SXSSFWorkbook implements Workbook
So why can this be a resource for using in try with resource? Because
Workbook.java:
public interface Workbook extends Closeable, Iterable<Sheet>
So org.apache.poi.ss.usermodel.Workbook extends java.io.Closeable and so classes which implements this must providing a method close.
SXSSFWorkbook.close
As you see, the single SheetDataWriters will be closed and then the internally XSSFWorkbook _wb will be closed.
SheetDataWriter.close
SheetDataWriter.close only flushes and closes the Writer _out.
So no, nowhere the dispose is called while auto closing until now (May 2018) in apache poiversion 3.17
And only SheetDataWriter.dispose will deleting the TempFile _fd created for each sheet.
This is a forrmal resolution of the problem.
SXSSFWorkbook t_wb = null;
try(SXSSFWorkbook wb = t_wb = new SXSSFWorkbook()) {
//...
} finally {
if(t_wb != null) t_wb.dispose();
}
This question bothers me too, so my solution is to override the close method, like this:
//a utility method somewhere
Workbook createMyCustomWorkbook() {
return new SXSSFWorkbook() {
public void close() throws IOException {
try {
dispose();
} catch (Exception e) {
//some logging
}
super.close();
}
};
}
//use in a simple try catch block
try(Workbook wb = createMyCustomWorkbook())
//do stuff with wb
}

Serenity+Cucumber: Reading testdata from Excel

I am running Automation test cases with #RunWith(CucumberWithSerenity.class).
We want to expose and maintain the Testdata separately in the Excel sheets instead of placing it in the Feature files.
The Template for Excel Testdata looks like:
|Scenario |UserName |Password|Name |Address|City |Pincode|
|Testcase1|testuser1|pass1 |testUser1|US |Jersy |12345 |
|Testcase1|testuser2|pass1 |testUser1|US |Virginia|78955 |
We have chose to use Primary Key as 'Scenario' which would be present in both Feature file and Excel sheet and based on that we will read the specific row from excel and refer the specific row data as Testdata for that particular Scenario.
Questions:
Is there a way to get the Scenario Name at run time from Feature file when Test is running, so that we can get the Excel sheet the extract the data for it from the Excel Sheets?
Is there a default way/method available in above mentioned use case, so that we can use it for above use case?
Cucumber doesn't support external sources by design (it is a collaboration tool, not a test automation tool). In Serenity, you can build a parameterised JUnit test that gets data from a CSV file: http://serenity-bdd.info/docs/serenity/#_using_test_data_from_csv_files
public class ExcelDataTable {
private XSSFWorkbook book;
private FileInputStream file;
public String ReadDataSheet(String page, String path, int rowValue, int cellValue) throws IOException {
String pointer;
file = new FileInputStream(new File(path));
book = new XSSFWorkbook(file);
Sheet sheet = book.getSheet(page);
Row row = sheet.getRow(rowValue);
Cell cell = row.getCell(cellValue);
pointer = cell.getStringCellValue();
book.close();
file.close();
return pointer;
}
}
Just create a class with this code and here is how I'm using it
public class OpenWebSite implements Task {
ExcelDataTable data = new ExcelDataTable();
public static OpenWebSite openWebSite(){
return Tasks.instrumented(OpenWebSite.class);
}
#Override
public <T extends Actor> void performAs(T actor) {
try {
actor.attemptsTo(Open.url(data.ReadDataSheet("info", "Data.xlsx", 1, 1)));
}
catch (IOException e) {
e.printStackTrace();
}
}
}
Sort that out to make yours bro

SXSSFWorkbook with Custom temp file

SXSSFWorkbook does what I want, but I would like to use a different type of temp file then what is provided and seemingly baked into the implementation.
In SheetDataWriter
public File createTempFile() throws IOException {
return TempFile.createTempFile("poi-sxssf-sheet", ".xml");
}
So...I can extend this by making a MySheetDataWriter and Overriding the call to createTempFile. However, there is no way to for me to use MySheetDataWriter in the SXSSFWorkbook...if I try to extendt it then the package protected method...could not be overidden, because it is not visible.
from SXSSFWorkbook
SheetDataWriter createSheetDataWriter() throws IOException {
if(_compressTmpFiles) {
return new GZIPSheetDataWriter(_sharedStringSource);
}
return new SheetDataWriter(_sharedStringSource);
}
So the bottom line is that I can use the implementation almost exactly as is, but I need a different kind of Temp file...not even just a different directory to put it in, but a completely different implementation. Any ideas on how to do this?
Starting at version 3.11, the createTempFile method you mention (from class TempFile) uses a replaceable TempFileCreationStrategy that can be chosen with the setTempFileCreationStrategy method.
The following example extends the default strategy to log every temp file that is created, but you could change it to return a custom File instance.
TempFile.setTempFileCreationStrategy(new TempFile.DefaultTempFileCreationStrategy() {
#Override
public File createTempFile(String prefix, String suffix) throws IOException {
File f = super.createTempFile(prefix, suffix);
log.debug("Created temp file: " + f);
return f;
}
});

I am unable to fetch excel data to selenium code At ubuntu o/s

public class ReadAndWrite {
public static void main(String[] args) throws InterruptedException, BiffException, IOException
{
System.out.println("hello");
ReadAndWrite.login();
}
public static void login() throws BiffException, IOException, InterruptedException{
WebDriver driver=new FirefoxDriver();
driver.get("URL");
System.out.println("hello");
FileInputStream fi = new FileInputStream("/home/sagarpatra/Desktop/Xpath.ods");
System.out.println("hiiiiiii");
Workbook w = Workbook.getWorkbook(fi);
Sheet sh = w.getSheet(1);
//or w.getSheet(Sheetnumber)
//String variable1 = s.getCell(column, row).getContents();
for(int row=1; row <=sh.getRows();row++)
{
String username = sh.getCell(0, row).getContents();
System.out.println("Username "+username);
driver.get("URL");
driver.findElement(By.name("Email")).sendKeys(username);
String password= sh.getCell(1, row).getContents();
System.out.println("Password "+password);
driver.findElement(By.name("Passwd")).sendKeys(password);
Thread.sleep(10000);
driver.findElement(By.name("Login")).click();
System.out.println("Waiting for page to load fully...");
Thread.sleep(30000);
}
driver.quit();
}
}
I don't know what is wrong with my code, or how to fix it. It outputs the following error:
Exception in thread "main" jxl.read.biff.BiffException: Unable to recognize OLE stream
at jxl.read.biff.CompoundFile.<init>(CompoundFile.java:116)
at jxl.read.biff.File.<init>(File.java:127)
at jxl.Workbook.getWorkbook(Workbook.java:221)
at jxl.Workbook.getWorkbook(Workbook.java:198)
at test.ReadTest.main(ReadTest.java:19)
I would try using Apache MetaModel instead. I have had better luck with that, than using JXL. Here is a example project I wrote that reads from a .XLSX file. I use this library to run tests on a Linux Jenkins server from .XLS files generated on MS Windows.
Also, it should be noted that this library is also perfect for making a parameterized DataProvider that queries a database with JDBC.
Using JXL, you limit yourself to one data type, either .XLS or .CSV. I believe MetaModel is actually using JXL under the hood and wrapping it to make it easier to use. So, it also would support the OpenOffice documents in the same fashion and suffer the same file compatibility issues.

apache-poi-3.9 + creating Dropdown

I am trying to create dropdown list in XLS using Apache-poi-3.9 .
Following code I have written ::
public class TestMacroTemplate {
/**
* #param args
* #throws IOException
*/
public static void main(String args[]) throws FileNotFoundException {
HSSFWorkbook workbook = new HSSFWorkbook();
HSSFSheet sheet = workbook.createSheet("Data Validation");
CellRangeAddressList addressList = new CellRangeAddressList(0, 0, 0, 0);
DVConstraint dvConstraint = DVConstraint
.createExplicitListConstraint(new String[] { "10", "20", "30" });
DataValidation dataValidation = new HSSFDataValidation(addressList,
dvConstraint);
dataValidation.setSuppressDropDownArrow(false);
sheet.addValidationData(dataValidation);
FileOutputStream fileOut = new FileOutputStream("XLCellDropDown.xls");
try {
workbook.write(fileOut);
fileOut.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
But it gives the following Exception :
Exception in thread "main" java.lang.NoSuchMethodError: org.apache.poi.hssf.usermodel.HSSFSheet.addValidationData(Lorg/apache/poi/ss/usermodel/DataValidation;)V
at ejb.TestMacroTemplate.main(TestMacroTemplate.java:31)
And the same code works with Apache-poi-3.2
Please help me.
Thanks ,
Nirav
Apache POI have a FAQ on this very problem. I'll quote from there, as it'll solve your problem
My code uses some new feature, compiles fine but fails when live with a "MethodNotFoundException", "NoSuchMethodError" or "IncompatibleClassChangeError"
You almost certainly have an older version of POI on your classpath. Quite a few runtimes and other packages will ship an older version of POI, so this is an easy problem to hit without your realising.
The best way to identify the offending earlier jar file is with a few lines of java. These will load one of the core POI classes, and report where it came from.
ClassLoader classloader =
org.apache.poi.poifs.filesystem.POIFSFileSystem.class.getClassLoader();
URL res = classloader.getResource(
"org/apache/poi/poifs/filesystem/POIFSFileSystem.class");
String path = res.getPath();
System.out.println("Core POI came from " + path);
It works fine in Apache poi 3.9 and i have tested it.just incluse these jars
poi-scratchpad-3.9-20121203.jar
poi-3.9-20121203.jar
poi-examples-3.9-20121203.jar
poi-excelant-3.9-20121203.jar
poi-ooxml-3.9-20121203.jar
poi-ooxml-schemas-3.9-20121203.jar

Resources