C# 在MediaTypeFormatter中处理匿名IEnumerable类型

C# 在MediaTypeFormatter中处理匿名IEnumerable类型,c#,asp.net-web-api,mediatypeformatter,C#,Asp.net Web Api,Mediatypeformatter,我遵循这一点,并能够实现我的自定义媒体类型格式化程序。它适用于已知类型,如产品、项目、地址等。但是,如果我有匿名JSON对象(如下),我想将其作为CSV下载,那么它将在Type itemType=Type.GetGenericArguments()[0]处失败抱怨 索引超出了数组的边界 感谢您的帮助 var _list = _dt.AsEnumerable().Select(r => new { LkpColCode = r.Field<string>("lkp_colum

我遵循这一点,并能够实现我的自定义媒体类型格式化程序。它适用于已知类型,如产品、项目、地址等。但是,如果我有匿名JSON对象(如下),我想将其作为CSV下载,那么它将在
Type itemType=Type.GetGenericArguments()[0]处失败抱怨

索引超出了数组的边界

感谢您的帮助

var _list = _dt.AsEnumerable().Select(r => new
{
  LkpColCode = r.Field<string>("lkp_column_code"),
  LkpColName = r.Field<string>("Description")
});

return _list;

/* [{"lkpColCode":"BUS","lkpColName":"Bus"},{"lkpColCode":"COM","lkpColName":"Community Bus"},
    {lkpColCode":"STC","lkpColName":"Streetcar"},{"lkpColCode":"SUB","lkpColName":"Subway"},
    {"lkpColCode":"TRC","lkpColName":"Trolley Coach"}]*/
var\u list=\u dt.AsEnumerable()。选择(r=>new
{
LkpColCode=r.字段(“lkp_列_代码”),
LkpColName=r.字段(“说明”)
});
返回列表;
/*[{“lkpColCode”:“总线”,“lkpColName”:“总线”},{“lkpColCode”:“COM”,“lkpColName”:“社区总线”},
{lkpColCode:“STC”,“lkpColName:“电车”},{“lkpColCode:“SUB”,“lkpColName:“地铁”},
{“lkpColCode”:“TRC”,“lkpColName”:“电车车厢”}]*/
编辑:完成下面的工作代码,可以处理除匿名之外的任何类型

public class CSVFormatter : MediaTypeFormatter
    {
        private string FileName { get; set; }

        public CSVFormatter()
        {
            SupportedMediaTypes.Add(new MediaTypeHeaderValue("text/csv"));

            SupportedEncodings.Add(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
            SupportedEncodings.Add(Encoding.GetEncoding("iso-8859-1"));
        }

        public CSVFormatter(MediaTypeMapping mediaTypeMapping)
            : this()
        {
            MediaTypeMappings.Add(mediaTypeMapping);

            SupportedEncodings.Add(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
            SupportedEncodings.Add(Encoding.GetEncoding("iso-8859-1"));
        }

        public CSVFormatter(IEnumerable<MediaTypeMapping> mediaTypeMappings)
            : this()
        {
            foreach (var mediaTypeMapping in mediaTypeMappings)
            {
                MediaTypeMappings.Add(mediaTypeMapping);
            }

            SupportedEncodings.Add(new UTF8Encoding(encoderShouldEmitUTF8Identifier: false));
            SupportedEncodings.Add(Encoding.GetEncoding("iso-8859-1"));
        }

        public override void SetDefaultContentHeaders(Type type, HttpContentHeaders headers, MediaTypeHeaderValue mediaType)
        {
            base.SetDefaultContentHeaders(type, headers, mediaType);
            headers.Add("Content-Disposition", string.Format("attachment; filename={0}", FileName));
        }

        public override MediaTypeFormatter GetPerRequestFormatterInstance(Type type, HttpRequestMessage request, MediaTypeHeaderValue mediaType)
        {
             //Usuage: In Controller Action:
            //if (!Request.Properties.ContainsKey("filename"))
            //Request.Properties.Add("filename", String.Format("SomeFileName_{0}.csv", DateTime.Now.ToString("yyyyMMdd-hhmmss")));

            if (request.Properties.ContainsKey("filename"))
            {
                FileName = request.Properties["filename"] as string;
            }
            else if (!String.IsNullOrWhiteSpace(FileName = request.GetQueryString("filename")))
            {
                FileName = FileName.CustomCompare(".csv") ? FileName : FileName + ".csv";
            }
            else
            {
                FileName = String.Format("Data-{0}.csv", DateTime.Now.ToString("yyyyMMdd-HHmmss"));
            }

            return this;
        }

        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 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 content)
        {
            //NOTE: We have check the type inside CanWriteType method. If request comes this far, the type is IEnumerable. We are safe. However it fails for Anonymous and errors out.

            Encoding effectiveEncoding = SelectCharacterEncoding(content.Headers);
            Type itemType = type.GetGenericArguments()[0];

            using (var writer = new StreamWriter(stream, effectiveEncoding))
            {
                //Write out columns
                writer.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)
                    {
                        var columnValue = Escape(val.Value);
                        _valueLine = string.Concat(_valueLine, columnValue, ",");
                    }

                    _valueLine = _valueLine.Substring(0, _valueLine.Length - 1);
                    writer.WriteLine(_valueLine);
                }
            }
        }

        #region Escape Characters
        static char[] _specialChars = new char[] { ',', '\n', '\r', '"' };

        private string Escape(object o)
        {
            if (o == null)
                return String.Empty;

            string field = o.ToString();

            // Delimit the entire field with quotes and replace embedded quotes with "".
            if (field.IndexOfAny(_specialChars) != -1)
                return String.Format("\"{0}\"", field.Replace("\"", "\"\""));
            else return field;

            //Quote forcefully
            //return String.Format("\"{0}\"", field.Replace("\"", "\"\""));
        }
        #endregion
    }
公共类CSVFormatter:MediaTypeFormatter
{
私有字符串文件名{get;set;}
公共CSVFormatter()
{
添加(新的MediaTypeHeaderValue(“text/csv”);
添加(新的UTF8Encoding(编码器应提交UTF8Identifier:false));
SupportedEncodings.Add(Encoding.GetEncoding(“iso-8859-1”);
}
公共CSVFormatter(MediaTypeMapping MediaTypeMapping)
:此()
{
添加(mediaTypeMapping);
添加(新的UTF8Encoding(编码器应提交UTF8Identifier:false));
SupportedEncodings.Add(Encoding.GetEncoding(“iso-8859-1”);
}
公共CSVFormatter(IEnumerable mediaTypeMappings)
:此()
{
foreach(mediaTypeMappings中的变量mediaTypeMapping)
{
添加(mediaTypeMapping);
}
添加(新的UTF8Encoding(编码器应提交UTF8Identifier:false));
SupportedEncodings.Add(Encoding.GetEncoding(“iso-8859-1”);
}
public override void SetDefaultContentHeaders(类型类型、HttpContentHeaders头、MediaTypeHeaderValue mediaType)
{
SetDefaultContentHeaders(类型、标题、媒体类型);
Add(“内容处置”,string.Format(“附件;文件名={0}”,文件名));
}
公共重写MediaTypeFormatter GetPerRequestFormatterInstance(类型类型、HttpRequestMessage请求、MediaTypeHeaderValue mediaType)
{
//用途:在控制器操作中:
//如果(!Request.Properties.ContainsKey(“文件名”))
//Add(“filename”,String.Format(“SomeFileName_{0}.csv”,DateTime.Now.ToString(“yyyyMMdd hhmmss”));
if(request.Properties.ContainsKey(“文件名”))
{
FileName=request.Properties[“FileName”]作为字符串;
}
如果(!String.IsNullOrWhiteSpace(FileName=request.GetQueryString(“FileName”)),则为else
{
FileName=FileName.CustomCompare(“.csv”)?FileName:FileName+“.csv”;
}
其他的
{
FileName=String.Format(“Data-{0}.csv”、DateTime.Now.ToString(“yyyyMMdd HHmmss”);
}
归还这个;
}
公共重写bool CanWriteType(类型)
{
if(type==null)
抛出新的ArgumentNullException(“类型”);
返回isTypeOfIEnumerable(类型);
}
私有bool isTypeOfIEnumerable(类型)
{
foreach(Type.GetInterfaces()中的类型interfaceType)
{
if(interfaceType==typeof(IEnumerable))
{返回true;}
}
返回false;
}
公共覆盖布尔CanReadType(类型)
{
返回false;
}
公共重写任务WriteToStreamAsync(类型类型、对象值、流、HttpContent内容、TransportContext)
{
writeStream(类型、值、流、内容);
var tcs=new TaskCompletionSource();
tcs.SetResult(0);
返回tcs.Task;
}
私有void writeStream(类型、对象值、流、HttpContent)
{
//注意:我们已经检查了CanWriteType方法中的类型。如果请求达到此程度,则类型为IEnumerable。我们是安全的。但是,它会因匿名和错误而失败。
编码有效编码=SelectCharacterEncoding(content.Headers);
Type itemType=Type.GetGenericArguments()[0];
使用(var writer=newstreamwriter(流,有效编码))
{
//写出专栏
WriteLine(string.Join(“,”,itemType.GetProperties().Select(x=>x.Name));
foreach(变量对象在(IEnumerable)值中)
{
var vals=obj.GetType().GetProperties().Select(pi=>new{Value=pi.GetValue(obj,null)});
string _valueLine=string.Empty;
foreach(var val中的var val)
{
var columnValue=转义(val.Value);
_valueLine=string.Concat(_valueLine,columnValue,“,”);
}
_valueLine=\u valueLine.Substring(0,\u valueLine.Length-1);
writer.WriteLine(_valueLine);
}
}
}
#区域转义字符
静态字符[]\u specialChars=新字符[]{',','\n','\r','''};
私有字符串转义(对象o)
{
如果(o==null)
返回字符串。空;
字符串字段=o.ToString();
//用引号分隔整个字段,并用“”替换嵌入的引号。
如果(字段索引(_specialChars)!=-1)
public class Thing
{
    public string LkpColCode {get;set;}
    public string LkpColName {get;set;}
}
private void WriteItem(Thing thing, StreamWriter writer)
{
    writer.WriteLine("{0},{1}", Escape(thing.LkpColCode), Escape(thing.LkpColName));
}