我遵循了这个great example,并且能够实现我的自定义媒体类型格式化程序。它适用于已知类型,例如产品,项目,地址等。但是如果我有匿名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"}]*/
编辑:下面的完整工作代码可以处理除匿名
之外的任何类型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
}
答案 0 :(得分:0)
首先它只是一个匿名类型,JSON与此无关。
其次,您正在使用的示例使用类型来确定它是否可以格式化数据。如果您使用的是匿名类型,则它无法正常工作。
最简单的路线是创建一个新类
public class Thing
{
public string LkpColCode {get;set;}
public string LkpColName {get;set;}
}
然后从示例中将Product
更改为Thing
(或您调用的任何内容),然后更改此代码
private void WriteItem(Thing thing, StreamWriter writer)
{
writer.WriteLine("{0},{1}", Escape(thing.LkpColCode), Escape(thing.LkpColName));
}
给它一个去看看你需要的地方。如果您需要更多帮助,可能需要使用更多代码更新您的问题。