views:

832

answers:

3

Hello!

I was just about to implement an override of ToString() on a particular business class in order to produce an Excel-friendly format to write to an output file, which will be picked up later and processed. Here's what the data is supposed to look like:

5555555 "LASTN SR, FIRSTN" 5555555555 13956 STREET RD  TOWNSVILLE MI 48890 25.88 01-003-06-0934

It's no big deal for me to just make a format string and override ToString(), but that will change the behavior of ToString() for any objects I decide to serialize this way, making the implementation of ToString() all ragged across the library.

Now, I've been reading up on IFormatProvider, and a class implementing it sounds like a good idea, but I'm still a little confused about where all this logic should reside and how to build the formatter class.

What do you guys do when you need to make a CSV, tab-delimited or some other non-XML arbitrary string out of an object?

+5  A: 

As rule of thumb I advocate only overriding toString as a tool for debugging, if it's for business logic it should be an explicit method on the class/interface.

For simple serialization like this I'd suggest having a separate class that knows about your CSV output library and your business objects that does the serialization rather than pushing the serialization into the business objects themselves.

This way you end up with a class per output format that produces a view of your model.

For more complex serialization where you're trying to write out an object graph for persistence I'd consider putting it in the business classes - but only if it makes for cleaner code.

Tom
I agree, I only override ToString() when I'm debugging. I would also create a separate serialization class but you can also add a property called "CSVString" that could build the string on the fly...
Joshua
A: 

My $0.02 is that you should not be overriding toString() in order to serialize these objects. Use a separate method that you write yourself. Call it serialize(), or marshall(), or pickle() - that last one if you're feeling Pythonic :) IMO, it's a little ugly, or maybe just less OO to use a separate class that knows how to serialize your objects. My instinct here is for each object to know how to serialize itself, but really it's up to you.

Matt Ball
+3  A: 

Here is a generic fashion for creating CSV from a list of objects, using reflection:

    public static string ToCsv<T>(string separator, IEnumerable<T> objectlist)
    {
        Type t = typeof(T);
        FieldInfo[] fields = t.GetFields();

        string header = String.Join(separator, fields.Select(f => f.Name).ToArray());

        StringBuilder csvdata = new StringBuilder();
        csvdata.AppendLine(header);

        foreach (var o in objectlist) 
            csvdata.AppendLine(ToCsvFields(separator, fields, o));

        return csvdata.ToString();
    }

    public static string ToCsvFields(string separator, FieldInfo[] fields, object o)
    {
        StringBuilder linie = new StringBuilder();

        foreach (var f in fields)
        {
            if (linie.Length > 0)
                linie.Append(separator);

            var x = f.GetValue(o);

            if (x != null)
                linie.Append(x.ToString());
        }

        return linie.ToString();
    }

Many variations can be made, such as writing out directly to a file in ToCsv(), or replacing the StringBuilder with an IEnumerable and yield statements.

Per Hejndorf