2017-08-16 96 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