2012-12-19 5 views
43

Często muszę serializować obiekt, zarówno do logowania lub debugowania. To jest serializacja w jedną stronę - nie muszę jej później odzyskiwać, wystarczy, że zmienię obiekt w ciąg znaków, aby go gdzieś napisać.W języku C#, w jaki sposób można w jedną stronę szeregować nieprzykrywalne?

Tak, tak - dlatego zawsze należy przesłonić metodę ToString. Wiem to. Ale często zajmuję się przedmiotami, których nie napisałem i nie mogę się zmienić. Ponadto nie chcę pisać i aktualizować metody ToString dla każdej klasy, którą piszę.

Serigrafia XML oferuje pozornie idealne rozwiązanie - wystarczy spłaszczyć obiekt do XML. Ale jest tak wiele ograniczeń, szczególnie że nie możesz serializować IDictionary i musisz mieć konstruktora bez parametrów. Mogę omijać je na moich zajęciach, ale - znowu - często pracuję z zajęciami innych ludzi.

Co to jest rozwiązanie, aby uzyskać kompleksową reprezentację ciągów obiektów? Czy jest coś prostego, czego mi brakuje?

+3

Może trochę refleksji do iteracji i renderowania członków na sznurku? Jest to jednak powolne i dlatego jest sensowne tylko w przypadku scenariuszy błędów, w których wydajność nie ma już znaczenia (więcej) ... –

Odpowiedz

38

Co powiesz na metodę rozszerzenia z własną logiką (i może trochę refleksji)?

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; 
    } 
} 

nazwać to tak:

someDefinedObject.OneWaySerialize(); 

Revisisons

  1. wersja początkowa
  2. Aktualizacja 12.26.2012
    • Dodano sprawdzić IEnumerable (dzięki aboveyou00)
    • Dodano sprawdzić anonimowego typu (i tylko etykieta to „nowe”, gdy wyjście)
+0

+1: Metoda rozszerzeń i odbicie byłyby moimi ulubionymi, zapewniając serializację tylko publicznie dostępnych właściwości. Może być trochę grząski dla dużych klas. –

+1

Oczywiście nie dla wrażliwych na wydajność, ale wydaje się bardziej jak narzędzie do debugowania niż narzędzie produkcyjne, więc odbicie powinno być satysfakcjonujące. –

+0

Absolutnie, po prostu umieszczając go tam na wypadek, gdyby OP zdecydował się umieścić go w mechanizmie rejestrowania dużych obiektów w ciężkim środowisku. Biorąc pod uwagę, że OP nie chce napisać jednego dla "każdego obiektu", przypuszczam, że planuje wykorzystać to "dużo". –

14

Jeśli jesteś wygodny do postaci szeregowej JSON, Json.NET jest doskonałym rozwiązaniem tego problemu.

Powiązane problemy