HiveBrain v1.2.0
Get Started
← Back to all entries
snippetcsharpMinor

How to avoid duplication in my document writer?

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
writerduplicationavoiddocumenthow

Problem

I have these two methods which are generating two different xml files whose header section is same and detail section varies. The root node is different for both xml files. Here I have two methods written, one for each. I want to optimize the code by avoiding the duplication of code in these two methods. Can you please guide me achieve this?

private string GenerateType1XML(TypeHeader header, List TypeDetailList)
{

    StringBuilder builder = new StringBuilder();
    using (StringWriter stringWriter = new StringWriter(builder))
    {
        using (XmlWriter writer = XmlWriter.Create(stringWriter))
        {
            writer.WriteStartDocument();
            writer.WriteStartElement(xmlElementConfig.CreateType1RootNode);
            WriteTypeHeader(writer, header);
            foreach (TypeDetail typ in TypeDetailList)
            {
                WriteType1Detail(writer, typ);
            }
            writer.WriteEndElement();
            writer.WriteEndDocument();
        }
    }

    return builder.ToString();

}

private string GenerateType2XML(TypeHeader header, List TypeDetailList)
{

    StringBuilder builder = new StringBuilder();
    using (StringWriter stringWriter = new StringWriter(builder))
    {
        using (XmlWriter writer = XmlWriter.Create(stringWriter))
        {
            writer.WriteStartDocument();
            writer.WriteStartElement(xmlElementConfig.CreateType2RootNode);
            WriteTypeHeader(writer, header);
            foreach (TypeDetail typ in TypeDetailList)
            {
                WriteType2Detail(writer, typ);
            }
            writer.WriteEndElement();
            writer.WriteEndDocument();
        }
    }

    return builder.ToString();

}

Solution

You need to look at what's common to both methods:

StringBuilder builder = new StringBuilder();
using (StringWriter stringWriter = new StringWriter(builder))
{
    using (XmlWriter writer = XmlWriter.Create(stringWriter))
    {
        writer.WriteStartDocument();
        writer.WriteStartElement(/* different in the two methods */);
        WriteTypeHeader(writer, header);
        foreach (TypeDetail typ in TypeDetailList)
        {
            /* different in the two methods */
        }
        writer.WriteEndElement();
        writer.WriteEndDocument();
    }
}


Looks like you could have a base class with a GenerateXml method calling into abstract members that derived types (Type1XmlGenerator, Type2XmlGenerator... whatever Type1 and Type2 means) can override:

public abstract class XmlGenerator
{
    protected abstract void WriteDetails(XmlWriter writer, TypeDetail type);
    protected abstract string XmlRootNode { get; }

    public string GenerateXml(TypeHeader header, IEnumerable details)
    {
        StringBuilder builder = new StringBuilder();
        using (StringWriter stringWriter = new StringWriter(builder))
        {
            using (XmlWriter writer = XmlWriter.Create(stringWriter))
            {
                writer.WriteStartDocument();
                writer.WriteStartElement(XmlRootNode);
                WriteTypeHeader(writer, header);
                foreach (TypeDetail detail in details)
                {
                    WriteDetails(writer, detail);
                }
                writer.WriteEndElement();
                writer.WriteEndDocument();
            }
        }
    }
}


Then you can have a class Type1XmlGenerator : XmlGenerator that will override the WriteDetails method with what you currently have in WriteType1Detail, and the XmlRootNode property getter implementation will return xmlElementConfig.CreateType1RootNode.

Now, writing/generating XML is a solved problem, doing that with a StringBuilder is error-prone. You should really look into what LINQ-to-XML (System.Xml.Linq) has in store for you.

Code Snippets

StringBuilder builder = new StringBuilder();
using (StringWriter stringWriter = new StringWriter(builder))
{
    using (XmlWriter writer = XmlWriter.Create(stringWriter))
    {
        writer.WriteStartDocument();
        writer.WriteStartElement(/* different in the two methods */);
        WriteTypeHeader(writer, header);
        foreach (TypeDetail typ in TypeDetailList)
        {
            /* different in the two methods */
        }
        writer.WriteEndElement();
        writer.WriteEndDocument();
    }
}
public abstract class XmlGenerator
{
    protected abstract void WriteDetails(XmlWriter writer, TypeDetail type);
    protected abstract string XmlRootNode { get; }

    public string GenerateXml(TypeHeader header, IEnumerable<TypeDetail> details)
    {
        StringBuilder builder = new StringBuilder();
        using (StringWriter stringWriter = new StringWriter(builder))
        {
            using (XmlWriter writer = XmlWriter.Create(stringWriter))
            {
                writer.WriteStartDocument();
                writer.WriteStartElement(XmlRootNode);
                WriteTypeHeader(writer, header);
                foreach (TypeDetail detail in details)
                {
                    WriteDetails(writer, detail);
                }
                writer.WriteEndElement();
                writer.WriteEndDocument();
            }
        }
    }
}

Context

StackExchange Code Review Q#55908, answer score: 5

Revisions (0)

No revisions yet.