Click here to Skip to main content
15,891,828 members
Articles / Programming Languages / C#
Tip/Trick

Custom CSV Formatter in ASP.NET Web API

Rate me:
Please Sign up or sign in to vote.
4.00/5 (6 votes)
24 Oct 2016CPOL 18K   7   2
Custom CSV formatter in ASP.NET Web API

Introduction

Well, we are quite dependent on XML and JSON responses. This tip illustrates how to create a custom CSV formatter in ASP.NET Web API.

Background

The web APIs mostly write XML or JSON responses to their output streams. Here is an alternative approach to write CSV response to web API's output stream.

Using the Code

Begin with a custom Class with the name CsvMediaTypeFormatter and inherit this class from MediaTypeFormatter. After that, override the basic method of MediaTypeFormatter to implement the required functionality.

C#
public class CsvMediaTypeFormatter : MediaTypeFormatter
    {
        public CsvMediaTypeFormatter()
        {
            this.SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv"));
            
            //SupportedEncodings.Add(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
            //SupportedEncodings.Add(Encoding.GetEncoding("iso-8859-1"));
        }

        public CsvMediaTypeFormatter(MediaTypeMapping mediaTypeMapping) : this()
        {
            MediaTypeMappings.Add(mediaTypeMapping);
        }
        
        public CsvMediaTypeFormatter(IEnumerable<MediaTypeMapping> mediaTypeMappings) : this()
        {
            foreach (var mediaTypeMapping in mediaTypeMappings)
            {
                MediaTypeMappings.Add(mediaTypeMapping);
            }
        }
        
        public override bool CanWriteType(Type type)
        {
            if (type == null)
                throw new ArgumentNullException("type");

            return isTypeOfIEnumerable(type);
        }

        private bool isTypeOfIEnumerable(Type type)
        {
            foreach (Type interfaceType in type.GetInterfaces())
            {
                if (interfaceType == typeof(IEnumerable))
                    return true;
            }

            return false;
        }

        public override bool CanReadType(Type type)
        {
            return false;
        }

        public override void SetDefaultContentHeaders
        (Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType)
        {
            base.SetDefaultContentHeaders(type, headers, mediaType);
            headers.Add("Content-Disposition", "attachment; 
            filename="+ "test_"+ 
            DateTime.Now.ToString("yyyyMMddhhmmssfff")+ ".csv");
        }
        
        public override Task WriteToStreamAsync(Type type, object value, 
        Stream stream, HttpContent content, TransportContext transportContext)
        {           
            writeStream(type, value, stream, content);
            var tcs = new TaskCompletionSource<int>();
            tcs.SetResult(0);
            return tcs.Task;
        }

        private void writeStream(Type type, object value, Stream stream, HttpContent contentHeaders)
        {            
            //NOTE: We have check the type inside CanWriteType method
            //If request comes this far, the type is IEnumerable. We are safe.
           
            Type itemType = type.GetGenericArguments()[0];

            StringWriter _stringWriter = new StringWriter();

            _stringWriter.WriteLine(
                string.Join<string>(
                    ",", itemType.GetProperties().Select(x => x.Name)
                )
            );

            foreach (var obj in (IEnumerable<object>)value)
            {

                var vals = obj.GetType().GetProperties().Select(
                    pi => new {
                        Value = pi.GetValue(obj, null)
                    }
                );

                string _valueLine = string.Empty;

                foreach (var val in vals)
                {
                    if (val.Value != null)
                    {
                        var _val = val.Value.ToString();

                        //Check if the value contains a comma and place it in quotes if so
                        if (_val.Contains(","))
                            _val = string.Concat("\"", _val, "\"");

                        //Replace any \r or \n special characters from a new line with a space
                        if (_val.Contains("\r"))
                            _val = _val.Replace("\r", " ");
                        if (_val.Contains("\n"))
                            _val = _val.Replace("\n", " ");

                        _valueLine = string.Concat(_valueLine, _val, ",");
                    }
                    else
                    {
                        _valueLine = string.Concat(string.Empty, ",");
                    }
                }

                _stringWriter.WriteLine(_valueLine.TrimEnd(','));
            }

            var streamWriter = new StreamWriter(stream);            
            streamWriter.Write(_stringWriter.ToString());
            streamWriter.Flush();
            streamWriter.Close();
        }        
    }

After creating CsvMediaTypeFormatter class, add the following code in Global.asax.cs file.

C#
GlobalConfiguration.Configuration.Formatters.Add(
                                     new CsvMediaTypeFormatter(
                                     new QueryStringMapping
                                     ("format", "csv", "text/csv")   ) );

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
India India
This member doesn't quite have enough reputation to be able to display their biography and homepage.

Comments and Discussions

 
QuestionExample please Pin
Member 1117239325-Oct-16 20:33
professionalMember 1117239325-Oct-16 20:33 
QuestionMy vote of 5 Pin
Farhad Reza24-Oct-16 6:40
Farhad Reza24-Oct-16 6:40 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.