How to read tables from a particular place in a document? - c#

When I use the below line It reads all tables of that particular document:
foreach (Microsoft.Office.Interop.Word.Table tableContent in document.Tables)
But I want to read tables of a particular content for example from one identifier to another identifier.
Identifier can be in the form of [SRS oraganisation_123] to another identifier [SRS Oraganisation_456]
I want to read the tables only in between the above mentioned identifiers.
Suppose 34th page contains my identifier so I want read all tables from that point to until I come across my second identifier. I don't want to read remaining tables.
Please ask me for any clarification in the question.

Say start and end Identifiers are stored in variables called myStartIdentifier and myEndIdentifier -
Range myRange = doc.Range();
int iTagStartIdx = 0;
int iTagEndIdx = 0;
if (myRange.Find.Execute(myStartIdentifier))
iTagStartIdx = myRange.Start;
myRange = doc.Range();
if (myRange.Find.Execute(myEndIdentifier))
iTagEndIdx = myRange.Start;
foreach (Table tbl in doc.Range(iTagStartIdx,iTagEndIdx).Tables)
{
// Your code goes here
}

Not sure how your program is structured... but if you can access the identifier in tableContent then you should be able to write a LINQ query.
var identifiers = new List<string>();
identifiers.Add("myIdentifier");
var tablesWithOnlyTheIdentifiersIWant = document.Tables.Select(tableContent => identifiers.Contains(tableContent.Identifier)
foreach(var tableContent in tablesWithOnlyTheIdentifiersIWant)
{
//Do something
}

Go through following code, if it helps you.
System.Data.DataTable dt = new System.Data.DataTable();
foreach (Microsoft.Office.Interop.Word.Cell c in r.Cells)
{
if(c.Range.Text=="Content you want to compare")
dt.Columns.Add(c.Range.Text);
}
foreach (Microsoft.Office.Interop.Word.Row row in newTable.Rows)
{
System.Data.DataRow dr = dt.NewRow();
int i = 0;
foreach (Cell cell in row.Cells)
{
if (!string.IsNullOrEmpty(cell.Range.Text)&&(cell.Range.Text=="Text you want to compare with"))
{
dr[i] = cell.Range.Text;
}
}
dt.Rows.Add(dr);
i++;
}
Go through following linked 3rd number answer.
Replace bookmark text in Word file using Open XML SDK

Related

In C# how do I go through a Google Sheets document and write into a specific cell

Following a tutorial, I have set up everything that needs to be set up for Google Sheets Api v4. In my Google Sheets documetnt, I have names of students in the first column, and in my second column I want to put their GPA. In my code, I made two variables that the user inputs, string name and string gpa. I want to go through column A, look for that name and insert that GPA next to it. I know I should probably use a for loop to go through the column, and compare every cell with the string the user typed, but nothing I tried so far worked.
I wrote a simple method that can get entries, for now it only prints but that can easily be changed:
static void ReadEntries()
{
var range = $"{sheet}!A1:F10";
var request = service.Spreadsheets.Values.Get(SpreadsheetId, range);
var response = request.Execute();
var values = response.Values;
if(values != null && values.Count > 0)
{
foreach(var row in values)
{
Console.WriteLine("{0} | {1}", row[0], row[1]);
}
}
else
{
Console.WriteLine("No data found");
}
}
and a method that can update a specific cell:
static void UpdateEntry()
{
var range = $"{sheet}!B2"; //example
var valueRange = new ValueRange();
var objectList = new List<object>() { "updated" };
valueRange.Values = new List<List<object>> { objectList };
var updateRequest = service.Spreadsheets.Values.Update(valueRange, SpreadsheetId, range);
updateRequest.ValueInputOption = SpreadsheetsResource.ValuesResource.AppendRequest.ValueInputOptionEnum.USERENTERED;
var updateResponse = updateRequest.Execute();
}
EDIT: I need help with making a for loop to go through my A column and find the student with the same name. I know how to update a cell. I just don't know how to find a cell that needs updating.
Sounds like you are very close. You already have the value you are searching in row[0] in the loop, so all you need to track the row number through your loop.
if (values != null && values.Count > 0)
{
int rowNo =0;
foreach (var row in values)
{
rowNo ++;
Console.WriteLine("{0} | {1}", row[0], row[1]);
if (row[0].ToString() == "John")
{
string rangeToUpdate = $"{sheet}!B{rowNo}:B{rowNo}";
...
}
}
}
You could also change from using a foreach to a standard for loop.
I'm not experienced in the .NET client library of the Sheets API.
However, having used the Sheets API with the node and python client libraries, I can point you to the documentation you should follow. This is the official API documentation, with code examples for each language having a Google-provided client library.
For example, here is the spreadsheets.values.update documentation that you use, with a code example for C#.
On to the question then:
According to the json representation of a ValueRange, ValueRange.Range does not seem optional even though it is redundant. You might need to add ValueRange.Range = range; in your code.
Plus, you are using SpreadsheetsResource.ValuesResource.AppendRequest instead of SpreadsheetsResource.ValuesResource.UpdateRequest in the definition of your ValueInputOption.
Let me know if it helped!
Update
This also seems to be a duplicate of Update a cell with C# and Sheets API V4

Select single table row using HtmlAgilityPack and iterate its links

I try to iterate a single table row and its a href links but it does not work as expected, instead of finding the selected row and its links it find all links in the table.. What am I doing wrong?
var allRows = doc.DocumentNode.SelectNodes("//table[#id='sortingTable']/tr");
var i = 0;
var rowNumber = 0;
foreach (var row in allRows)
{
if (row.InnerText.Contains("Text in cell for which row I want to use"))
{
rowNumber = i+1;
break;
}
i += 1;
}
var list = new List<SortFile>();
var rowToRead = allRows[rowNumber]; // One specific row
var numberOfLinks = rowToRead.SelectNodes("//a[#href]"); // this does not find the 2 links in the table row but all links in the whole table?
foreach (HtmlNode link in rowToRead.SelectNodes("//a[#href]"))
{
//HtmlAttribute att = link.Attributes["href"];
//var text = link.OuterHtml;
}
The XPath you are using (//a[#href]) would get all of the links in the document. // means to find anything starting from the document root.
You should use .//a[#href] to start from the current node and select all links. That would only take the links underneath the tr node you have selected.

Is there a way to dynamically create an object at run time in .NET 3.5?

I'm working on an importer that takes tab delimited text files. The first line of each file contains 'columns' like ItemCode, Language, ImportMode etc and there can be varying numbers of columns.
I'm able to get the names of each column, whether there's one or 10 and so on. I use a method to achieve this that returns List<string>:
private List<string> GetColumnNames(string saveLocation, int numColumns)
{
var data = (File.ReadAllLines(saveLocation));
var columnNames = new List<string>();
for (int i = 0; i < numColumns; i++)
{
var cols = from lines in data
.Take(1)
.Where(l => !string.IsNullOrEmpty(l))
.Select(l => l.Split(delimiter.ToCharArray(), StringSplitOptions.None))
.Select(value => string.Join(" ", value))
let split = lines.Split(' ')
select new
{
Temp = split[i].Trim()
};
foreach (var x in cols)
{
columnNames.Add(x.Temp);
}
}
return columnNames;
}
If I always knew what columns to be expecting, I could just create a new object, but since I don't, I'm wondering is there a way I can dynamically create an object with properties that correspond to whatever GetColumnNames() returns?
Any suggestions?
For what it's worth, here's how I used DataTables to achieve what I wanted.
// saveLocation is file location
// numColumns comes from another method that gets number of columns in file
var columnNames = GetColumnNames(saveLocation, numColumns);
var table = new DataTable();
foreach (var header in columnNames)
{
table.Columns.Add(header);
}
// itemAttributeData is the file split into lines
foreach (var row in itemAttributeData)
{
table.Rows.Add(row);
}
Although there was a bit more work involved to be able to manipulate the data in the way I wanted, Karthik's suggestion got me on the right track.
You could create a dictionary of strings where the first string references the "properties" name and the second string its characteristic.

Adding text to multiples rows in word with a single bookmark

Is it possible to add several rows with the help of Bookmarks and openXML to a word document?
We have a worddocument that serves as a report template.
In that template we need to add several transaction rows.
The problem is that the number of rows aren't static. It could be 0, 1 or 42 for example.
In the current template (which we can change) we have added 3 bookmarks
TransactionPart, TransactionPart2 and TransactionPart3.
The tree transactionparts forms a singel row with three different datacontent (ID, Description, Amount)
If we have just one transaction row we have no problem adding the data to those bookmarks, but what do we do when we should add row two? There are no bookmarks for more rows.
Is there a smart way of doing this?
Or should we change the worddocument so that the rows end up in a table? Would that solve the problem in a better way?
I would put a single bookmark lets call it "transactions" inside a 3 coloumn table.
Like this
When you know the design of the table, but not the number of rows you'll be needing the simplest way is to add a row for each line of data you have.
You could accomplish that with a code like this
//make some data.
List<String[]> data = new List<string[]>();
for (int i = 0; i < 10; i++)
data.Add(new String[] {"this","is","sparta" });
using (WordprocessingDocument wordDoc = WordprocessingDocument.Open("yourDocument.docx", true))
{
var mainPart = wordDoc.MainDocumentPart;
var bookmarks = mainPart.Document.Body.Descendants<BookmarkStart>();
var bookmark =
from n in bookmarks
where n.Name == "transactions"
select n;
OpenXmlElement elem = bookmark.First().Parent;
//isolate tabel
while (!(elem is DocumentFormat.OpenXml.Wordprocessing.Table))
elem = elem.Parent;
var table = elem; //found
//save the row you wanna copy in each time you have data.
var oldRow = elem.Elements<TableRow>().Last();
DocumentFormat.OpenXml.Wordprocessing.TableRow row = (TableRow)oldRow.Clone();
//remove old row
elem.RemoveChild<TableRow>(oldRow);
foreach (String[] s in data)
{
DocumentFormat.OpenXml.Wordprocessing.TableRow newrow = (TableRow)row.Clone();
var cells = newrow.Elements<DocumentFormat.OpenXml.Wordprocessing.TableCell>();
//we know we have 3 cells
for(int i = 0; i < cells.Count(); i++)
{
var c = cells.ElementAt(i);
var run = c.Elements<Paragraph>().First().Elements<Run>().First();
var text = run.Elements<Text>().First();
text.Text = s[i];
}
table.AppendChild(newrow);
}
}
You end up with this
I've tested this code on a pretty basic document and know it works.
Good luck and let me know if I can clarify further.

How to By pass first Row on Excel in foreach loop?

I have this Code for Reading Excell Records:
public IEnumerable<FillinEntity> Map(IEnumerable<ExcelRow> excelRows)
{
List<FillinEntity> fillinEntities = new List<FillinEntity>();
foreach (ExcelRow row in excelRows)
{
FillinEntity excell = new FillinEntity();
excell.SerialNumber = Convert.ToString(row.Cells[0]);
excell.PalletNumber = Convert.ToString(row.Cells[1]);
excell.Location = Convert.ToString(row.Cells[2]);
excell.CreatedBy = Convert.ToString(row.Cells[3]);
fillinEntities.Add(excell);
}
return fillinEntities;
}
I have this records: And it succesfully inserted
R03091294 2 2 FGROOM RYAN
My Problem: I Add column header on the excell sheet.
Serial Number Pallet Location CreatedBy -----> i need to by pass column header.
R03091294 2 2 FGROOM RYAN
Thanks in regards
You could always just skip it:
foreach (ExcelRow row in excelRows.Cast<ExcelRow>().Skip(1))
See Skip().
Note: I used Cast<ExcelRow>() in case your enumerable excelRows can't be resolved to ExcelRow.
bool is_first_row = True;
foreach (ExcelRow row in excelRows)
{
if(is_first_row)
{
is_first_row = false;
continue;
}
...
}
....
.Skip(1)
solution provided by Codesleuth is much better option.

Categories

Resources