public static GenerationResult SearchAndReplace(string document)
        {
            var unfilledTokens = new UnfilfilledTokens();

            using (var templateStream = new MemoryStream())
            {
                File.Open(document, FileMode.Open).CopyTo(templateStream);
                templateStream.Position = 0;

                using (var wordDoc = WordprocessingDocument.Open(templateStream, isEditable: true))
                {
                    var mainDocument = wordDoc.MainDocumentPart;

                    //// https://stackoverflow.com/questions/31750228/replacing-text-of-content-controls-in-openxml#answer-31755783
                    var elements = mainDocument.Document.Body.Descendants <SdtElement>()
                                   .Concat(mainDocument.HeaderParts.SelectMany(x => x.Header.Descendants <SdtElement>()))
                                   .Concat(mainDocument.FooterParts.SelectMany(x => x.Footer.Descendants <SdtElement>()))
                                   .ToList();
                    //ReplaceTokensByName(elements, unfilledTokens);

                    ReplaceTokensByTag(elements, unfilledTokens);

                    wordDoc.SaveAs("./output.docx");
                }
            }

            return(new GenerationResult
            {
                UnfilledTokens = unfilledTokens
            });
        }
        public static void ReplaceTokensByName(IEnumerable <SdtElement> fields, UnfilfilledTokens unfilledTokens)
        {
            foreach (var sdtElement in fields)
            {
                var alias = sdtElement.Descendants <SdtAlias>().FirstOrDefault();
                if (alias == null)
                {
                    continue;
                }

                var tokenName = alias.Val.Value;

                if (!Mappings.TryGetValue(tokenName, out Func <string> mappedValue))
                {
                    unfilledTokens.Add(tokenName);
                    continue;
                }

                ReplaceElementValue(sdtElement, mappedValue());
            }
        }
        // https://msdn.microsoft.com/en-us/library/office/gg605189(v=office.14).aspx
        public static void ReplaceTokensByTag(IEnumerable <SdtElement> fields, UnfilfilledTokens unfilledTokens)
        {
            var tokens = fields
                         .Select(x => new
            {
                Element = x,
                Tag     = x.SdtProperties.GetFirstChild <Tag>()
            })
                         .Where(x => x.Tag != null)
                         .Select(x => new
            {
                x.Element,
                Name = x.Tag.Val.Value
            })
                         .GroupBy(x => x.Name)
                         .Select(g => new
            {
                Name     = g.Key,
                Elements = g.Select(x => x.Element)
            })
                         .ToList();

            foreach (var token in tokens)
            {
                Console.WriteLine("   Replacing token {0} x{1}", token.Name, token.Elements.Count());

                if (token.Name == "table1")
                {
                    // https://msdn.microsoft.com/en-us/library/cc197932(office.12).aspx
                    var sdtElement = token.Elements.First();
                    var theTable   = sdtElement.Descendants <Table>().Single();
                    var theRow     = theTable.Elements <TableRow>().Last();

                    foreach (var data in new[]
                    {
                        new[] { "1", "2", "3" },
                        new[] { "10", "20", "30" }
                    })
                    {
                        var rowCopy = (TableRow)theRow.CloneNode(true);
                        var cells   = rowCopy.Descendants <TableCell>();
                        for (var i = 0; i < data.Length; i++)
                        {
                            cells.ElementAt(i).Append(new Paragraph(new Run(new Text(data[i]))));
                        }
                        //rowCopy.Descendants<TableCell>().ElementAt(0).Append(new Paragraph
                        //    (new Run(new Text(data.Contact.ToString()))));
                        //rowCopy.Descendants<TableCell>().ElementAt(1).Append(new Paragraph
                        //    (new Run(new Text(data.NameOfProduct.ToString()))));
                        //rowCopy.Descendants<TableCell>().ElementAt(2).Append(new Paragraph
                        //    (new Run(new Text(data.Amount.ToString()))));
                        theTable.AppendChild(rowCopy);
                    }
                    theTable.RemoveChild(theRow);
                    RemoveContentControl(sdtElement);
                    continue;
                }

                if (token.Name == "repeat1")
                {
                    // https://msdn.microsoft.com/en-us/library/cc197932(office.12).aspx
                    var sdtElement = token.Elements.First();

                    var repeat = sdtElement.Descendants <SdtRepeatedSection>().First();
                    //var theTable = sdtElement.Descendants<Table>().Single();
                    //var theRow = theTable.Elements<TableRow>().Last();

                    foreach (var data in new[]
                    {
                        new[] { "1", "2", "3" },
                        new[] { "10", "20", "30" }
                    })
                    {
                        var clone = repeat.CloneNode(true);
                        clone.InsertAfterSelf(repeat);
                        //repeat.InsertBefore()
                        //ReplaceTokensByTag(sdtElement.Descendants<SdtElement>(), unfilledTokens);
                    }
                    //theTable.RemoveChild(theRow);
                    RemoveContentControl(sdtElement);
                    continue;
                }

                if (!Mappings.TryGetValue(token.Name, out Func <string> mappedValue))
                {
                    unfilledTokens.Add(token.Name, token.Elements.Count());
                    continue;
                }

                ReplaceElementValue(token.Elements, mappedValue());
            }
        }