2012-12-19 119 views
43

很多時候,我需要序列化一個對象,用於記錄或調試。這是一種單向序列化 - 我不需要稍後再將它取出,我只需要將對象轉換爲字符串以便將其寫入某處即可。在C#中,你如何單向序列化不可序列化?

是的,是的 - 這就是爲什麼你應該總是重寫ToString方法。我知道這個。但是我經常處理我沒有寫和不能改變的對象。另外,我不想爲每個我寫的類編寫和更新一個ToString方法。

XML序列化提供了一個看似完美的解決方案 - 只是將該對象平鋪到XML中。但有很多限制,特別是你不能序列化IDictionary,你必須有一個無參數的構造函數。我可以在課堂上解決這些問題,但是 - 我又經常和其他人一起上課。

那麼,獲取對象的全面字符串表示的解決方案是什麼?有沒有簡單的我錯過了?

+3

也許一些反射迭代和呈現成員字符串?然而,這是緩慢的,因此只有在性能無關緊要的情況下才有意義... –

回答

38

如何使用自己的邏輯(也許一些反射)的擴展方法?

public static class SerializerExtension 
{ 
    public static String OneWaySerialize(this Object obj) 
    { 
     if (Object.ReferenceEquals(obj, null)) 
     { 
      return "NULL"; 
     } 
     if (obj.GetType().IsPrimitive || obj.GetType() == typeof(String)) 
     { 
      if (obj is String) 
       return String.Format("\"{0}\"", obj); 
      if (obj is Char) 
       return String.Format("'{0}'", obj); 
      return obj.ToString(); 
     } 

     StringBuilder builder = new StringBuilder(); 
     Type objType = obj.GetType(); 
     if (IsEnumerableType(objType)) 
     { 
      builder.Append("["); 

      IEnumerator enumerator = ((IEnumerable)obj).GetEnumerator(); 
      Boolean moreElements = enumerator.MoveNext(); 
      while (moreElements) 
      { 
       builder.Append(enumerator.Current.OneWaySerialize()); 
       moreElements = enumerator.MoveNext(); 
       if (moreElements) 
       { 
        builder.Append(","); 
       } 
      } 

      builder.Append("]"); 
     } 
     else 
     { 
      builder.AppendFormat("{0} {{ ", IsAnonymousType(objType) ? "new" : objType.Name); 

      PropertyInfo[] properties = objType.GetProperties(); 
      for (Int32 p = properties.Length; p > 0; p--) 
      { 
       PropertyInfo prop = properties[p-1]; 
       String propName = prop.Name; 
       Object propValue = prop.GetValue(obj, null); 
       builder.AppendFormat("{0} = {1}", propName, propValue.OneWaySerialize()); 
       if (p > 1) 
       { 
        builder.Append(", "); 
       } 
      } 

      builder.Append(" }"); 
     } 

     return builder.ToString(); 
    } 

    // http://stackoverflow.com/a/2483054/298053 
    private static Boolean IsAnonymousType(Type type) 
    { 
     if (type == null) 
     { 
      return false; 
     } 
     return Attribute.IsDefined(type, typeof(System.Runtime.CompilerServices.CompilerGeneratedAttribute), false) 
      && type.IsGenericType && type.Name.Contains("AnonymousType") 
      && (type.Name.StartsWith("<>") || type.Name.StartsWith("VB$")) 
      && (type.Attributes & TypeAttributes.NotPublic) == TypeAttributes.NotPublic; 
    } 

    private static Boolean IsEnumerableType(Type type) 
    { 
     if (type == null) 
     { 
      return false; 
     } 
     foreach (Type intType in type.GetInterfaces()) 
     { 
      if (intType.GetInterface("IEnumerable") != null || (intType.IsGenericType && intType.GetGenericTypeDefinition() == typeof(IEnumerable<>))) 
      { 
       return true; 
      } 
     } 
     return false; 
    } 
} 

調用它像這樣:

someDefinedObject.OneWaySerialize(); 

Revisisons

  1. 初始版本
  2. 更新2012年12月26日
    • 新增支票的IEnumerable(感謝aboveyou00)
    • 新增支票匿名類型(和公正的標註爲「新」時輸出)
+0

+1:擴展方法和反射將成爲我的最愛,確保只序列化公開可用的屬性。雖然對於大型班級可能會有點麻煩。 –

+1

顯然不是對性能敏感,但它看起來更像是一個調試工具而不是生產工具,所以反射應該是_satisfactory_。 –

+0

當然,如果OP決定將它放入大型物體的日誌機制中,那麼就放在那裏吧。鑑於OP不想爲「每個對象」寫一個,我猜他打算用這個「很多」。 –

14

如果你舒適的序列化到JSON,Json.NET是一個很好的解決這個問題。