2017-08-16 84 views
-1

我遵循此great example并能够实现我的自定义媒体类型格式化程序。它为已知类型,如产品,项目,地址等。但是,如果我有匿名JSON对象(如下图),我想下载为CSV那么它失败在Type itemType = type.GetGenericArguments()[0];抱怨处理MediaTypeFormatter中的匿名IEnumerable类型

指数外的伟大工程数组的边界。

任何帮助表示赞赏。

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

_then失败._如何?请更具体一些。 –

+0

@MartinLiversage请参阅最新的代码。我正在尝试处理此媒体格式化程序中的匿名类型。 – programmerboy

回答

0

首先,它只是一个匿名类型,JSON无关与此有关。

其次,您正在使用的示例使用类型来判断它是否可以格式化数据。如果您使用的是匿名类型,则无法正常工作。

最简单的途径是创建一个新的类

public class Thing 
{ 
    public string LkpColCode {get;set;} 
    public string LkpColName {get;set;} 
} 

再从如换ProductThing(或者无论你怎么称呼它),然后更改此代码

private void WriteItem(Thing thing, StreamWriter writer) 
{ 
    writer.WriteLine("{0},{1}", Escape(thing.LkpColCode), Escape(thing.LkpColName)); 
} 

给该去一看,看看它在哪里。如果你需要更多的帮助,你可能想用更多的代码来更新你的问题。

+0

正如我在我的问题中提到的,我的代码可以处理已知类型,所以此解决方案并不完全有帮助。我特别想知道是否有办法处理匿名类型,因为有时您需要在Anonymous对象中发送数据。请参阅更新的问题。 – programmerboy