2012-12-19 42 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是一个很好的解决这个问题。