2012-12-19 9 views
43

多くの場合、ロギングまたはデバッグのためにオブジェクトをシリアル化する必要があります。これは一方通行のシリアライゼーションです。後で取り戻す必要はありません。オブジェクトをどこかに書き込むために文字列に変換するだけで済みます。C#では、直列化できないシリアル化を一方向でシリアル化する方法はありますか?

はい、はい - これは、常にToStringメソッドをオーバーライドする必要がある理由です。私はこれを知っている。しかし、私は書かなかったオブジェクトを扱うことが多く、変更することはできません。さらに、私は書くすべてのクラスのToStringメソッドを作成し、更新する必要はありません。

XMLシリアル化は、一見完璧なソリューションを提供します。そのオブジェクトをXMLにフラット化するだけです。しかし、非常に多くの制限があります。具体的にはIDictionaryをシリアル化できないため、パラメータのないコンストラクタが必要です。私は授業でこれらを取り上げることができますが、私はしばしば他の人のクラスと一緒に仕事をしています。

オブジェクトの包括的な文字列表現を得るにはどうすればよいですか?私は行方不明の何か簡単ですか?

+3

多分、反復してメンバを文字列にレンダリングするためのリフレクションですか?しかし、それは遅いので、性能が重要ではないエラーシナリオでは賢明です。 –

答えて

38

独自のロジック(および多分いくつかのReflection)を使った拡張方法はどうですか?

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. 初期バージョン2012年12月26日
    • のIEnumerableのための追加のチェック(感謝aboveyou00)更新
    • を追加しました匿名型のチェック(およびそれをラベル「新しい」出力)
+0

+1:拡張メソッドとリフレクションはここで私のお気に入りになります。公開されているプロパティのみをシリアル化してください。しかし、大規模なクラスのために少しぼろぼろになるかもしれません。 –

+1

明らかにパフォーマンスの影響を受けませんが、プロダクションよりもデバッグツールに似ているように思えます。 –

+0

OPは、重い使用環境の大きなオブジェクトのロギングメカニズムにOPを入れることにした場合には、そこに置いてください。 OPが "すべてのオブジェクト"のために1つを書くことを望まないとすれば、彼はこれを "たくさん"使う予定だと思います。 –

14

あなたがJSONにシリアライズ快適であれば、Json.NETは、この問題に対する最適なソリューションです。

関連する問題