I have the following program that opens a Word document template using the OpenXML library and replaces a couple of phrases with their counterparts from what will be a database (right now its just dummy data). Something about the if-else struture in the nested foreach
loops bothers me. Is there a better way I can accomplish this task? Would regular expressions be a more viable option?
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System;
namespace OpenXmlSearchExample
{
class Program
{
static void Main(string[] args)
{
string templatePath = @"C:\users\example\desktop\template.dotx";
string resultPath = @"C:\users\example\desktop\OpenXmlExample.docx";
using (WordprocessingDocument document = WordprocessingDocument.CreateFromTemplate(templatePath))
{
var body = document.MainDocumentPart.Document.Body;
var paragraphs = body.Elements<Paragraph>();
// Iterate through paragraphs, runs, and text, finding the text we want and replacing it
foreach (Paragraph paragraph in paragraphs)
{
foreach (Run run in paragraph.Elements<Run>())
{
foreach (Text text in run.Elements<Text>())
{
if (text.Text == "Plan")
{
text.Text = string.Format("{0} {1} Plan", DateTime.Now.Year, "Q2");
}
else if (text.Text == "Project Name")
{
text.Text = "SUPER SECRET CODE NAME";
}
else if (text.Text == "WO-nnnn Name")
{
text.Text = "Maintenance";
}
else
{
Console.WriteLine(text.Text);
Console.ReadKey();
}
}
}
}
// Save result document, not modifying the template
document.SaveAs(resultPath);
}
}
}
}
2 Answers 2
You can
- replace inner
foreach
loops with LINQ - replace
if-else
withswitch
- replace
string.Format
with string interpolation - add
const
modifier for path variables if they will be constants - remove
string[] args
fromMain
if you won't use them
Result
using DocumentFormat.OpenXml.Packaging;
using DocumentFormat.OpenXml.Wordprocessing;
using System;
namespace OpenXmlSearchExample
{
class Program
{
static void Main()
{
const string templatePath = @"C:\users\example\desktop\template.dotx";
const string resultPath = @"C:\users\example\desktop\OpenXmlExample.docx";
using (WordprocessingDocument document = WordprocessingDocument.CreateFromTemplate(templatePath))
{
var body = document.MainDocumentPart.Document.Body;
var paragraphs = body.Elements<Paragraph>();
var texts = paragraphs.SelectMany(p => p.Elements<Run>()).SelectMany(r => r.Elements<Text>());
foreach (Text text in texts)
{
switch (text.Text)
{
case "Plan":
text.Text = $"{DateTime.Now.Year} Q2 Plan";
break;
case "Project Name":
text.Text = "SUPER SECRET CODE NAME";
break;
case "WO-nnnn Name":
text.Text = "Maintenance";
break;
default:
Console.WriteLine(text.Text);
Console.ReadKey();
break;
}
}
// Save result document, not modifying the template
document.SaveAs(resultPath);
}
}
}
}
-
\$\begingroup\$ Thanks for the response! I love LINQ, I'm just too new to use it effectively. \$\endgroup\$Ellis– Ellis2018年06月26日 15:03:23 +00:00Commented Jun 26, 2018 at 15:03
-
\$\begingroup\$ How can I install this library. I used this nuget.org/packages/DocumentFormat.OpenXml/%20 , but the CreateFromTemplate and SaveAs methods are not recognized \$\endgroup\$eddy– eddy2019年01月23日 10:52:13 +00:00Commented Jan 23, 2019 at 10:52
Using case-sensitive string matching like that is in my opinion the first nail in the coffin. Instead a case-insensitive dictionary would be a much better option.
Example:
var replacements = new Dictionary<string, Func<string>>(StringComparer.OrdinalIgnoreCase)
{
["Plan"] = () => $"{DateTime.Now.Year} Q2 Plan",
["Project Name"] = () => $"SUPER SECRET CODE NAME",
};
text.Text = replacements[text.Text]();
This is also much easier to extend/maintain.
You shouldn't mix full type names with var
. You can use var
not only for normal variables but also inside using
statements or foreach
loops. Keep it consistent.
-
\$\begingroup\$ How can I install this library? I used this nuget.org/packages/DocumentFormat.OpenXml/%20 , but the CreateFromTemplate and SaveAs methods are not recognized \$\endgroup\$eddy– eddy2019年01月23日 10:52:33 +00:00Commented Jan 23, 2019 at 10:52
-
1\$\begingroup\$ Instructions can be found in Nuget: nuget.org/packages/DocumentFormat.OpenXml \$\endgroup\$james.garriss– james.garriss2021年06月21日 12:33:45 +00:00Commented Jun 21, 2021 at 12:33