public XMergeCell(XMergeCell mergeCell)
 {
     this.Width     = mergeCell.Width;
     this.Height    = mergeCell.Height;
     this.X1        = mergeCell.X1;
     this.Y1        = mergeCell.Y1;
     this.X2        = mergeCell.X2;
     this.Y2        = mergeCell.Y2;
     this.MergeCell = mergeCell.MergeCell;
 }
        private void GetMercells(XmlDocument doc, XmlNode worksheet)
        {
            var mergeCells = doc.SelectSingleNode($"/x:worksheet/x:mergeCells", _ns);

            if (mergeCells != null)
            {
                var newMergeCells = mergeCells.Clone();
                //mergeCells.RemoveAll();
                worksheet.RemoveChild(mergeCells);

                foreach (XmlElement cell in newMergeCells)
                {
                    var @ref       = cell.Attributes["ref"].Value;
                    var mergerCell = new XMergeCell(cell);
                    this.XMergeCellInfos[mergerCell.XY1] = mergerCell;
                }
            }
        }
        private void WriteSheetXml(Stream stream, XmlDocument doc, XmlNode sheetData)
        {
            //Q.Why so complex?
            //A.Because try to use string stream avoid OOM when rendering rows
            sheetData.RemoveAll();
            sheetData.InnerText = "{{{{{{split}}}}}}";                                            //TODO: bad code smell
            var prefix    = string.IsNullOrEmpty(sheetData.Prefix) ? "" : $"{sheetData.Prefix}:";
            var endPrefix = string.IsNullOrEmpty(sheetData.Prefix) ? "" : $":{sheetData.Prefix}"; //![image](https://user-images.githubusercontent.com/12729184/115000066-fd02b300-9ed4-11eb-8e65-bf0014015134.png)
            var contents  = doc.InnerXml.Split(new string[] { $"<{prefix}sheetData>{{{{{{{{{{{{split}}}}}}}}}}}}</{prefix}sheetData>" }, StringSplitOptions.None);;

            using (var writer = new StreamWriter(stream, Encoding.UTF8))
            {
                writer.Write(contents[0]);
                writer.Write($"<{prefix}sheetData>"); // prefix problem

                #region Generate rows and cells
                int originRowIndex;
                int rowIndexDiff = 0;
                foreach (var rowInfo in XRowInfos)
                {
                    var row = rowInfo.Row;

                    //TODO: some xlsx without r
                    originRowIndex = int.Parse(row.GetAttribute("r"));
                    var newRowIndex = originRowIndex + rowIndexDiff;



                    if (rowInfo.CellIEnumerableValues != null)
                    {
                        var first            = true;
                        var iEnumerableIndex = 0;
                        foreach (var item in rowInfo.CellIEnumerableValues)
                        {
                            iEnumerableIndex++;

                            var newRow = row.Clone() as XmlElement;
                            newRow.SetAttribute("r", newRowIndex.ToString());
                            newRow.InnerXml = row.InnerXml.Replace($"{{{{$rowindex}}}}", newRowIndex.ToString());

                            if (rowInfo.IsDictionary)
                            {
                                var dic = item as IDictionary <string, object>;
                                foreach (var propInfo in rowInfo.PropsMap)
                                {
                                    var key = $"{{{{{rowInfo.IEnumerablePropName}.{propInfo.Key}}}}}";
                                    if (item == null) //![image](https://user-images.githubusercontent.com/12729184/114728510-bc3e5900-9d71-11eb-9721-8a414dca21a0.png)
                                    {
                                        newRow.InnerXml = newRow.InnerXml.Replace(key, "");
                                        continue;
                                    }

                                    var cellValue = dic[propInfo.Key];
                                    if (cellValue == null)
                                    {
                                        newRow.InnerXml = newRow.InnerXml.Replace(key, "");
                                        continue;
                                    }


                                    var cellValueStr = ExcelOpenXmlUtils.EncodeXML(cellValue?.ToString());
                                    var type         = propInfo.Value.UnderlyingTypePropType;
                                    if (type == typeof(bool))
                                    {
                                        cellValueStr = (bool)cellValue ? "1" : "0";
                                    }
                                    else if (type == typeof(DateTime))
                                    {
                                        //c.SetAttribute("t", "d");
                                        cellValueStr = ((DateTime)cellValue).ToString("yyyy-MM-dd HH:mm:ss");
                                    }

                                    //TODO: ![image](https://user-images.githubusercontent.com/12729184/114848248-17735880-9e11-11eb-8258-63266bda0a1a.png)
                                    newRow.InnerXml = newRow.InnerXml.Replace(key, cellValueStr);
                                }
                            }
                            else if (rowInfo.IsDataTable)
                            {
                                var datarow = item as DataRow;
                                foreach (var propInfo in rowInfo.PropsMap)
                                {
                                    var key = $"{{{{{rowInfo.IEnumerablePropName}.{propInfo.Key}}}}}";
                                    if (item == null) //![image](https://user-images.githubusercontent.com/12729184/114728510-bc3e5900-9d71-11eb-9721-8a414dca21a0.png)
                                    {
                                        newRow.InnerXml = newRow.InnerXml.Replace(key, "");
                                        continue;
                                    }

                                    var cellValue = datarow[propInfo.Key];
                                    if (cellValue == null)
                                    {
                                        newRow.InnerXml = newRow.InnerXml.Replace(key, "");
                                        continue;
                                    }


                                    var cellValueStr = ExcelOpenXmlUtils.EncodeXML(cellValue?.ToString());
                                    var type         = propInfo.Value.UnderlyingTypePropType;
                                    if (type == typeof(bool))
                                    {
                                        cellValueStr = (bool)cellValue ? "1" : "0";
                                    }
                                    else if (type == typeof(DateTime))
                                    {
                                        //c.SetAttribute("t", "d");
                                        cellValueStr = ((DateTime)cellValue).ToString("yyyy-MM-dd HH:mm:ss");
                                    }

                                    //TODO: ![image](https://user-images.githubusercontent.com/12729184/114848248-17735880-9e11-11eb-8258-63266bda0a1a.png)
                                    newRow.InnerXml = newRow.InnerXml.Replace(key, cellValueStr);
                                }
                            }
                            else
                            {
                                foreach (var propInfo in rowInfo.PropsMap)
                                {
                                    var prop = propInfo.Value.PropertyInfo;

                                    var key = $"{{{{{rowInfo.IEnumerablePropName}.{prop.Name}}}}}";
                                    if (item == null) //![image](https://user-images.githubusercontent.com/12729184/114728510-bc3e5900-9d71-11eb-9721-8a414dca21a0.png)
                                    {
                                        newRow.InnerXml = newRow.InnerXml.Replace(key, "");
                                        continue;
                                    }

                                    var cellValue = prop.GetValue(item);
                                    if (cellValue == null)
                                    {
                                        newRow.InnerXml = newRow.InnerXml.Replace(key, "");
                                        continue;
                                    }


                                    var cellValueStr = ExcelOpenXmlUtils.EncodeXML(cellValue?.ToString());
                                    var type         = Nullable.GetUnderlyingType(prop.PropertyType) ?? prop.PropertyType;
                                    if (type == typeof(bool))
                                    {
                                        cellValueStr = (bool)cellValue ? "1" : "0";
                                    }
                                    else if (type == typeof(DateTime))
                                    {
                                        //c.SetAttribute("t", "d");
                                        cellValueStr = ((DateTime)cellValue).ToString("yyyy-MM-dd HH:mm:ss");
                                    }

                                    //TODO: ![image](https://user-images.githubusercontent.com/12729184/114848248-17735880-9e11-11eb-8258-63266bda0a1a.png)
                                    newRow.InnerXml = newRow.InnerXml.Replace(key, cellValueStr);
                                }
                            }

                            // note: only first time need add diff ![image](https://user-images.githubusercontent.com/12729184/114494728-6bceda80-9c4f-11eb-9685-8b5ed054eabe.png)
                            if (!first)
                            {
                                //rowIndexDiff++;
                                rowIndexDiff = rowIndexDiff + (rowInfo.IEnumerableMercell == null ? 1 : rowInfo.IEnumerableMercell.Height); //TODO:base on the merge size
                            }
                            first = false;

                            var mergeBaseRowIndex = newRowIndex;
                            newRowIndex = newRowIndex + (rowInfo.IEnumerableMercell == null ? 1 : rowInfo.IEnumerableMercell.Height);
                            writer.Write(CleanXml(newRow.OuterXml, endPrefix));
                            newRow = null;

                            //mergecells
                            if (rowInfo.RowMercells != null)
                            {
                                foreach (var mergeCell in rowInfo.RowMercells)
                                {
                                    var newMergeCell = new XMergeCell(mergeCell);
                                    newMergeCell.Y1 = newMergeCell.Y1 + rowIndexDiff;
                                    newMergeCell.Y2 = newMergeCell.Y2 + rowIndexDiff;
                                    this.NewXMergeCellInfos.Add(newMergeCell);
                                }

                                // Last merge one don't add new row, or it'll get duplicate result like : https://github.com/shps951023/MiniExcel/issues/207#issuecomment-824550950
                                if (iEnumerableIndex == rowInfo.CellIEnumerableValuesCount)
                                {
                                    continue;
                                }

                                // https://github.com/shps951023/MiniExcel/issues/207#issuecomment-824518897
                                for (int i = 1; i < rowInfo.IEnumerableMercell.Height; i++)
                                {
                                    mergeBaseRowIndex++;
                                    var _newRow = row.Clone() as XmlElement;
                                    _newRow.SetAttribute("r", mergeBaseRowIndex.ToString());

                                    var cs = _newRow.SelectNodes($"x:c", _ns);
                                    // all v replace by empty
                                    // TODO: remove c/v
                                    foreach (XmlElement _c in cs)
                                    {
                                        _c.RemoveAttribute("t");
                                        foreach (XmlNode ch in _c.ChildNodes)
                                        {
                                            _c.RemoveChild(ch);
                                        }
                                    }

                                    _newRow.InnerXml = _newRow.InnerXml.Replace($"{{{{$rowindex}}}}", mergeBaseRowIndex.ToString());
                                    writer.Write(CleanXml(_newRow.OuterXml, endPrefix));
                                }
                            }
                        }
                    }
                    else
                    {
                        row.SetAttribute("r", newRowIndex.ToString());
                        row.InnerXml = row.InnerXml.Replace($"{{{{$rowindex}}}}", newRowIndex.ToString());
                        writer.Write(CleanXml(row.OuterXml, endPrefix));

                        //mergecells
                        if (rowInfo.RowMercells != null)
                        {
                            foreach (var mergeCell in rowInfo.RowMercells)
                            {
                                var newMergeCell = new XMergeCell(mergeCell);
                                newMergeCell.Y1 = newMergeCell.Y1 + rowIndexDiff;
                                newMergeCell.Y2 = newMergeCell.Y2 + rowIndexDiff;
                                this.NewXMergeCellInfos.Add(newMergeCell);
                            }
                        }
                    }

                    // get the row's all mergecells then update the rowindex
                }
                #endregion

                writer.Write($"</{prefix}sheetData>");

                if (this.NewXMergeCellInfos.Count != 0)
                {
                    writer.Write($"<{prefix}mergeCells count=\"{this.NewXMergeCellInfos.Count}\">");
                    foreach (var cell in this.NewXMergeCellInfos)
                    {
                        writer.Write(cell.ToXmlString(prefix));
                    }
                    writer.Write($"</{prefix}mergeCells>");
                }

                writer.Write(contents[1]);
            }
        }