如何使用 openXML 深度克隆 .xlsx 文件中的行?
How can I deep clone row in .xlsx file using openXML?
我有 template.xlsx 文件,我必须通过在特定索引中添加一行的几个副本来修改它。当我尝试使用克隆方法进行此操作时,我添加了一行,但每一行都相互修改。我需要创建 opemxml 行对象的深层克隆,但是当我尝试这样做时,出现错误,指出 openxml 行对象未序列化。我如何使用带有序列化的 openXML 深度克隆 .xlsx 文件中的行,或者是否有其他方法可以深度克隆 openxml 行对象?
您可以在 OpenXmlElement
上使用 .CloneNode(true)
进行深度克隆
所以如果你想在 table 中复制一行,它看起来像
// suppose table an OpenXml Table and row the row you want to clone
table.Append(row.CloneNode(true));
编辑:
在特定行之前插入它
// suppose r is the row you want to insert it before
r.InsertBeforeSelf(row.CloneNode(true));
在我的例子中,我需要在 sheet 的末尾复制几行。
我确实需要克隆特定范围的行,即值、样式和公式、合并的单元格。在复制和粘贴几行的问题上花了很多时间,我终于找到了解决办法。我能够复制第 18 行到第 26 行并从第 27 行粘贴它们。
示例如下:
代码如下:
public static void CopyRowRange(SpreadsheetDocument document, string sheetName,
int srcRowFrom, int srcRowTo, int destRowFrom)
{
WorkbookPart workbookPart = document.WorkbookPart;
if (srcRowTo < srcRowFrom || destRowFrom < srcRowFrom) return;
int destRowFromBase = destRowFrom;
WorksheetPart worksheetPart = GetWorksheetPartByName(document, sheetName);
SheetData sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();
IList<Cell> cells = sheetData.Descendants<Cell>().Where(c =>
GetRowIndex(c.CellReference) >= srcRowFrom &&
GetRowIndex(c.CellReference) <= srcRowTo).ToList<Cell>();
if (cells.Count() == 0) return;
int copiedRowCount = srcRowTo - srcRowFrom + 1;
MoveRowIndex(document, sheetName, destRowFrom - 1, srcRowTo, srcRowFrom);
IDictionary<int, IList<Cell>> clonedCells = null;
IList<Cell> formulaCells = new List<Cell>();
IList<Row> cloneRelatedRows = new List<Row>();
destRowFrom = destRowFromBase;
int changedRowsCount = destRowFrom - srcRowFrom;
formulaCells.Clear();
clonedCells = new Dictionary<int, IList<Cell>>();
foreach (Cell cell in cells)
{
Cell newCell = (Cell)cell.CloneNode(true);
int index = Convert.ToInt32(GetRowIndex(cell.CellReference));
int rowIndex = index - changedRowsCount;
newCell.CellReference = GetColumnName(cell.CellReference) + rowIndex.ToString();
IList<Cell> rowCells = null;
if (clonedCells.ContainsKey(rowIndex))
rowCells = clonedCells[rowIndex];
else
{
rowCells = new List<Cell>();
clonedCells.Add(rowIndex, rowCells);
}
rowCells.Add(newCell);
if (newCell.CellFormula != null && newCell.CellFormula.Text.Length > 0)
{
formulaCells.Add(newCell);
}
}
foreach (int rowIndex in clonedCells.Keys)
{
Row row = sheetData.Elements<Row>().Where(r => r.RowIndex == rowIndex).FirstOrDefault();
if (row == null)
{
row = new Row() { RowIndex = (uint)rowIndex };
Row refRow = sheetData.Elements<Row>().Where(r => r.RowIndex > rowIndex).OrderBy(r => r.RowIndex).FirstOrDefault();
if (refRow == null)
sheetData.AppendChild<Row>(row);
else
sheetData.InsertBefore<Row>(row, refRow);
}
row.Append(clonedCells[rowIndex].ToArray());
cloneRelatedRows.Add(row);
}
ChangeFormulaRowNumber(worksheetPart.Worksheet, formulaCells, changedRowsCount);
foreach (Row row in cloneRelatedRows)
{
IList<Cell> cs = row.Elements<Cell>().OrderBy(c => c.CellReference.Value).ToList<Cell>();
row.RemoveAllChildren();
row.Append(cs.ToArray());
}
MergeCells mcells = worksheetPart.Worksheet.GetFirstChild<MergeCells>();
if (mcells != null)
{
IList<MergeCell> newMergeCells = new List<MergeCell>();
IEnumerable<MergeCell> clonedMergeCells = mcells.Elements<MergeCell>().
Where(m => MergeCellInRange(m, srcRowFrom, srcRowTo)).ToList<MergeCell>();
foreach (MergeCell cmCell in clonedMergeCells)
{
MergeCell newMergeCell = CreateChangedRowMergeCell(worksheetPart.Worksheet, cmCell, changedRowsCount);
newMergeCells.Add(newMergeCell);
}
uint count = mcells.Count.Value;
mcells.Count = new UInt32Value(count + (uint)newMergeCells.Count);
mcells.Append(newMergeCells.ToArray());
}
}
private static WorksheetPart
GetWorksheetPartByName(SpreadsheetDocument document,
string sheetName)
{
IEnumerable<Sheet> sheets =
document.WorkbookPart.Workbook.GetFirstChild<Sheets>().
Elements<Sheet>().Where(s => s.Name == sheetName);
if (sheets.Count() == 0)
{
return null;
}
string relationshipId = sheets.First().Id.Value;
WorksheetPart worksheetPart = (WorksheetPart)
document.WorkbookPart.GetPartById(relationshipId);
return worksheetPart;
}
private static void MoveRowIndex(SpreadsheetDocument document, string sheetName, int destRowFrom, int srcRowTo, int srcRowFrom)
{
WorksheetPart worksheetPart = GetWorksheetPartByName(document, sheetName);
SheetData sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();
uint newRowIndex;
IEnumerable<Row> rows = sheetData.Descendants<Row>().Where(r => r.RowIndex.Value >= srcRowFrom && r.RowIndex.Value <= srcRowTo);
foreach (Row row in rows)
{
newRowIndex = Convert.ToUInt32(destRowFrom + 1);
foreach (Cell cell in row.Elements<Cell>())
{
string cellReference = cell.CellReference.Value;
cell.CellReference = new StringValue(cellReference.Replace(row.RowIndex.Value.ToString(), newRowIndex.ToString()));
}
row.RowIndex = new UInt32Value(newRowIndex);
destRowFrom++;
}
}
private static void ChangeFormulaRowNumber(Worksheet worksheet, IList<Cell> formulaCells, int changedRowsCount)
{
foreach (Cell formulaCell in formulaCells)
{
Regex regex = new Regex(@"\d+");
var rowIndex = Convert.ToInt32(regex.Match(formulaCell.CellReference).Value);
Regex regex2 = new Regex("[A-Za-z]+");
var columnIndex = regex2.Match(formulaCell.CellReference).Value;
int newRowIndex = rowIndex + changedRowsCount;
Cell cell = GetCell(worksheet, columnIndex, newRowIndex);
cell.CellFormula = new CellFormula(cell.CellFormula.Text.Replace($"{rowIndex}",$"{newRowIndex}"));
}
}
private static MergeCell CreateChangedRowMergeCell(Worksheet worksheet, MergeCell cmCell, int changedRows)
{
string[] cells = cmCell.Reference.Value.Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
Regex regex = new Regex(@"\d+");
var rowIndex1 = Convert.ToInt32(regex.Match(cells[0]).Value);
var rowIndex2 = Convert.ToInt32(regex.Match(cells[1]).Value);
Regex regex2 = new Regex("[A-Za-z]+");
var columnIndex1 = regex2.Match(cells[0]).Value;
var columnIndex2 = regex2.Match(cells[1]).Value;
var cell1Name = $"{columnIndex1}{rowIndex1 + changedRows}";
var cell2Name = $"{columnIndex2}{rowIndex2 + changedRows}";
CreateSpreadsheetCellIfNotExist(worksheet, cell1Name);
CreateSpreadsheetCellIfNotExist(worksheet, cell2Name);
return new MergeCell() { Reference = new StringValue(cell1Name + ":" + cell2Name) };
}
private static bool MergeCellInRange(MergeCell mergeCell, int srcRowFrom, int srcRowTo)
{
string[] cells = mergeCell.Reference.Value.Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
Regex regex = new Regex(@"\d+");
var cellIndex1 = Convert.ToInt32(regex.Match(cells[0]).Value);
var cellIndex2 = Convert.ToInt32(regex.Match(cells[1]).Value);
if (srcRowFrom <= cellIndex1 && cellIndex1 <= srcRowTo &&
srcRowFrom <= cellIndex2 && cellIndex2 <= srcRowTo)
return true;
else
return false;
}
private static uint GetRowIndex(string cellName)
{
Regex regex = new Regex(@"\d+");
Match match = regex.Match(cellName);
return uint.Parse(match.Value);
}
private static string GetColumnName(string cellName)
{
Regex regex = new Regex("[A-Za-z]+");
Match match = regex.Match(cellName);
return match.Value;
}
private static void CreateSpreadsheetCellIfNotExist(Worksheet worksheet, string cellName)
{
string columnName = GetColumnName(cellName);
uint rowIndex = GetRowIndex(cellName);
IEnumerable<Row> rows = worksheet.Descendants<Row>().Where(r => r.RowIndex.Value == rowIndex);
if (rows.Count() == 0)
{
Row row = new Row() { RowIndex = new UInt32Value(rowIndex) };
Cell cell = new Cell() { CellReference = new StringValue(cellName) };
row.Append(cell);
worksheet.Descendants<SheetData>().First().Append(row);
worksheet.Save();
}
else
{
Row row = rows.First();
IEnumerable<Cell> cells = row.Elements<Cell>().Where(c => c.CellReference.Value == cellName);
if (cells.Count() == 0)
{
Cell cell = new Cell() { CellReference = new StringValue(cellName) };
row.Append(cell);
worksheet.Save();
}
}
}
private static Cell GetCell(Worksheet worksheet, string columnIndex, int newRowIndex)
{
string cellName = columnIndex + newRowIndex;
Row row = worksheet.GetFirstChild<SheetData>().Descendants<Row>().FirstOrDefault(r => r.RowIndex.Value == newRowIndex);
return row.Elements<Cell>().FirstOrDefault(c => c.CellReference.Value == cellName);
}
我有 template.xlsx 文件,我必须通过在特定索引中添加一行的几个副本来修改它。当我尝试使用克隆方法进行此操作时,我添加了一行,但每一行都相互修改。我需要创建 opemxml 行对象的深层克隆,但是当我尝试这样做时,出现错误,指出 openxml 行对象未序列化。我如何使用带有序列化的 openXML 深度克隆 .xlsx 文件中的行,或者是否有其他方法可以深度克隆 openxml 行对象?
您可以在 OpenXmlElement
上使用.CloneNode(true)
进行深度克隆
所以如果你想在 table 中复制一行,它看起来像
// suppose table an OpenXml Table and row the row you want to clone
table.Append(row.CloneNode(true));
编辑: 在特定行之前插入它
// suppose r is the row you want to insert it before
r.InsertBeforeSelf(row.CloneNode(true));
在我的例子中,我需要在 sheet 的末尾复制几行。 我确实需要克隆特定范围的行,即值、样式和公式、合并的单元格。在复制和粘贴几行的问题上花了很多时间,我终于找到了解决办法。我能够复制第 18 行到第 26 行并从第 27 行粘贴它们。
示例如下:
代码如下:
public static void CopyRowRange(SpreadsheetDocument document, string sheetName,
int srcRowFrom, int srcRowTo, int destRowFrom)
{
WorkbookPart workbookPart = document.WorkbookPart;
if (srcRowTo < srcRowFrom || destRowFrom < srcRowFrom) return;
int destRowFromBase = destRowFrom;
WorksheetPart worksheetPart = GetWorksheetPartByName(document, sheetName);
SheetData sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();
IList<Cell> cells = sheetData.Descendants<Cell>().Where(c =>
GetRowIndex(c.CellReference) >= srcRowFrom &&
GetRowIndex(c.CellReference) <= srcRowTo).ToList<Cell>();
if (cells.Count() == 0) return;
int copiedRowCount = srcRowTo - srcRowFrom + 1;
MoveRowIndex(document, sheetName, destRowFrom - 1, srcRowTo, srcRowFrom);
IDictionary<int, IList<Cell>> clonedCells = null;
IList<Cell> formulaCells = new List<Cell>();
IList<Row> cloneRelatedRows = new List<Row>();
destRowFrom = destRowFromBase;
int changedRowsCount = destRowFrom - srcRowFrom;
formulaCells.Clear();
clonedCells = new Dictionary<int, IList<Cell>>();
foreach (Cell cell in cells)
{
Cell newCell = (Cell)cell.CloneNode(true);
int index = Convert.ToInt32(GetRowIndex(cell.CellReference));
int rowIndex = index - changedRowsCount;
newCell.CellReference = GetColumnName(cell.CellReference) + rowIndex.ToString();
IList<Cell> rowCells = null;
if (clonedCells.ContainsKey(rowIndex))
rowCells = clonedCells[rowIndex];
else
{
rowCells = new List<Cell>();
clonedCells.Add(rowIndex, rowCells);
}
rowCells.Add(newCell);
if (newCell.CellFormula != null && newCell.CellFormula.Text.Length > 0)
{
formulaCells.Add(newCell);
}
}
foreach (int rowIndex in clonedCells.Keys)
{
Row row = sheetData.Elements<Row>().Where(r => r.RowIndex == rowIndex).FirstOrDefault();
if (row == null)
{
row = new Row() { RowIndex = (uint)rowIndex };
Row refRow = sheetData.Elements<Row>().Where(r => r.RowIndex > rowIndex).OrderBy(r => r.RowIndex).FirstOrDefault();
if (refRow == null)
sheetData.AppendChild<Row>(row);
else
sheetData.InsertBefore<Row>(row, refRow);
}
row.Append(clonedCells[rowIndex].ToArray());
cloneRelatedRows.Add(row);
}
ChangeFormulaRowNumber(worksheetPart.Worksheet, formulaCells, changedRowsCount);
foreach (Row row in cloneRelatedRows)
{
IList<Cell> cs = row.Elements<Cell>().OrderBy(c => c.CellReference.Value).ToList<Cell>();
row.RemoveAllChildren();
row.Append(cs.ToArray());
}
MergeCells mcells = worksheetPart.Worksheet.GetFirstChild<MergeCells>();
if (mcells != null)
{
IList<MergeCell> newMergeCells = new List<MergeCell>();
IEnumerable<MergeCell> clonedMergeCells = mcells.Elements<MergeCell>().
Where(m => MergeCellInRange(m, srcRowFrom, srcRowTo)).ToList<MergeCell>();
foreach (MergeCell cmCell in clonedMergeCells)
{
MergeCell newMergeCell = CreateChangedRowMergeCell(worksheetPart.Worksheet, cmCell, changedRowsCount);
newMergeCells.Add(newMergeCell);
}
uint count = mcells.Count.Value;
mcells.Count = new UInt32Value(count + (uint)newMergeCells.Count);
mcells.Append(newMergeCells.ToArray());
}
}
private static WorksheetPart
GetWorksheetPartByName(SpreadsheetDocument document,
string sheetName)
{
IEnumerable<Sheet> sheets =
document.WorkbookPart.Workbook.GetFirstChild<Sheets>().
Elements<Sheet>().Where(s => s.Name == sheetName);
if (sheets.Count() == 0)
{
return null;
}
string relationshipId = sheets.First().Id.Value;
WorksheetPart worksheetPart = (WorksheetPart)
document.WorkbookPart.GetPartById(relationshipId);
return worksheetPart;
}
private static void MoveRowIndex(SpreadsheetDocument document, string sheetName, int destRowFrom, int srcRowTo, int srcRowFrom)
{
WorksheetPart worksheetPart = GetWorksheetPartByName(document, sheetName);
SheetData sheetData = worksheetPart.Worksheet.GetFirstChild<SheetData>();
uint newRowIndex;
IEnumerable<Row> rows = sheetData.Descendants<Row>().Where(r => r.RowIndex.Value >= srcRowFrom && r.RowIndex.Value <= srcRowTo);
foreach (Row row in rows)
{
newRowIndex = Convert.ToUInt32(destRowFrom + 1);
foreach (Cell cell in row.Elements<Cell>())
{
string cellReference = cell.CellReference.Value;
cell.CellReference = new StringValue(cellReference.Replace(row.RowIndex.Value.ToString(), newRowIndex.ToString()));
}
row.RowIndex = new UInt32Value(newRowIndex);
destRowFrom++;
}
}
private static void ChangeFormulaRowNumber(Worksheet worksheet, IList<Cell> formulaCells, int changedRowsCount)
{
foreach (Cell formulaCell in formulaCells)
{
Regex regex = new Regex(@"\d+");
var rowIndex = Convert.ToInt32(regex.Match(formulaCell.CellReference).Value);
Regex regex2 = new Regex("[A-Za-z]+");
var columnIndex = regex2.Match(formulaCell.CellReference).Value;
int newRowIndex = rowIndex + changedRowsCount;
Cell cell = GetCell(worksheet, columnIndex, newRowIndex);
cell.CellFormula = new CellFormula(cell.CellFormula.Text.Replace($"{rowIndex}",$"{newRowIndex}"));
}
}
private static MergeCell CreateChangedRowMergeCell(Worksheet worksheet, MergeCell cmCell, int changedRows)
{
string[] cells = cmCell.Reference.Value.Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
Regex regex = new Regex(@"\d+");
var rowIndex1 = Convert.ToInt32(regex.Match(cells[0]).Value);
var rowIndex2 = Convert.ToInt32(regex.Match(cells[1]).Value);
Regex regex2 = new Regex("[A-Za-z]+");
var columnIndex1 = regex2.Match(cells[0]).Value;
var columnIndex2 = regex2.Match(cells[1]).Value;
var cell1Name = $"{columnIndex1}{rowIndex1 + changedRows}";
var cell2Name = $"{columnIndex2}{rowIndex2 + changedRows}";
CreateSpreadsheetCellIfNotExist(worksheet, cell1Name);
CreateSpreadsheetCellIfNotExist(worksheet, cell2Name);
return new MergeCell() { Reference = new StringValue(cell1Name + ":" + cell2Name) };
}
private static bool MergeCellInRange(MergeCell mergeCell, int srcRowFrom, int srcRowTo)
{
string[] cells = mergeCell.Reference.Value.Split(':', 2, StringSplitOptions.RemoveEmptyEntries);
Regex regex = new Regex(@"\d+");
var cellIndex1 = Convert.ToInt32(regex.Match(cells[0]).Value);
var cellIndex2 = Convert.ToInt32(regex.Match(cells[1]).Value);
if (srcRowFrom <= cellIndex1 && cellIndex1 <= srcRowTo &&
srcRowFrom <= cellIndex2 && cellIndex2 <= srcRowTo)
return true;
else
return false;
}
private static uint GetRowIndex(string cellName)
{
Regex regex = new Regex(@"\d+");
Match match = regex.Match(cellName);
return uint.Parse(match.Value);
}
private static string GetColumnName(string cellName)
{
Regex regex = new Regex("[A-Za-z]+");
Match match = regex.Match(cellName);
return match.Value;
}
private static void CreateSpreadsheetCellIfNotExist(Worksheet worksheet, string cellName)
{
string columnName = GetColumnName(cellName);
uint rowIndex = GetRowIndex(cellName);
IEnumerable<Row> rows = worksheet.Descendants<Row>().Where(r => r.RowIndex.Value == rowIndex);
if (rows.Count() == 0)
{
Row row = new Row() { RowIndex = new UInt32Value(rowIndex) };
Cell cell = new Cell() { CellReference = new StringValue(cellName) };
row.Append(cell);
worksheet.Descendants<SheetData>().First().Append(row);
worksheet.Save();
}
else
{
Row row = rows.First();
IEnumerable<Cell> cells = row.Elements<Cell>().Where(c => c.CellReference.Value == cellName);
if (cells.Count() == 0)
{
Cell cell = new Cell() { CellReference = new StringValue(cellName) };
row.Append(cell);
worksheet.Save();
}
}
}
private static Cell GetCell(Worksheet worksheet, string columnIndex, int newRowIndex)
{
string cellName = columnIndex + newRowIndex;
Row row = worksheet.GetFirstChild<SheetData>().Descendants<Row>().FirstOrDefault(r => r.RowIndex.Value == newRowIndex);
return row.Elements<Cell>().FirstOrDefault(c => c.CellReference.Value == cellName);
}