6
\$\begingroup\$

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);
            }
        }
    }
}
\$\endgroup\$
5
\$\begingroup\$

You can

  • replace inner foreach loops with LINQ
  • replace if-else with switch
  • replace string.Format with string interpolation
  • add const modifier for path variables if they will be constants
  • remove string[] args from Main 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);
            }
        }
    }
}
\$\endgroup\$
  • \$\begingroup\$ Thanks for the response! I love LINQ, I'm just too new to use it effectively. \$\endgroup\$ – Ellis Jun 26 '18 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 Jan 23 at 10:52
6
\$\begingroup\$

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.

\$\endgroup\$
  • \$\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 Jan 23 at 10:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.