2013-10-03 88 views
0

我正在嘗試使用通用序列化代理來控制通過使用NetDataContractSerializer序列化的字段的命名。這個概念迄今爲止運行良好,除了我遇到一個字段,該字段在調用AddValue的GetObjectData方法中持有對集合的引用,併爲其提供集合類型似乎導致沒有項目在序列化的集合中。我需要爲數組或列表等集合做些特別的事情嗎?如何在使用自定義ISerializationSurrogate時序列化嵌套集合?

我已經在下面的示例中添加了我的完整代碼的副本,希望有人能夠指出我的正確方向。

static void Main() 
{ 
    var obj = new Club(
     "Fight Club", 
     Enumerable.Range(1, 3).Select(i => new Member() { Age = i, Name = i.ToString() })); 

    var streamingContext = new StreamingContext(StreamingContextStates.Clone); 
    //var serializer = new NetDataContractSerializer(streamingContext); 
    var serializer = new NetDataContractSerializer(streamingContext, int.MaxValue, false, System.Runtime.Serialization.Formatters.FormatterAssemblyStyle.Full, new MySurrogateSelector(streamingContext)); 
    var ms = new MemoryStream(); 
    serializer.Serialize(ms, obj); 

    Console.WriteLine("Before serializing: \n{0}\n", obj); 

    ms.Position = 0; 
    var xml = XElement.Load(ms); 
    Console.WriteLine("Serialized object: \n\n{0}\n", xml); 

    ms.Position = 0; 
    var deserializedObj = serializer.Deserialize(ms); 

    Console.WriteLine("After deserializing: \n{0}\n", deserializedObj); 
    Console.ReadKey(); 
} 

public class MySurrogateSelector : ISurrogateSelector 
{ 
    private ISerializationSurrogate _surrogate = new BackingFieldSerializationSurrogate(); 

    public MySurrogateSelector(StreamingContext streamingContext) 
    { 
    } 

    public void ChainSelector(ISurrogateSelector selector) 
    { 
     throw new System.NotImplementedException(); 
    } 

    public ISurrogateSelector GetNextSelector() 
    { 
     throw new System.NotImplementedException(); 
    } 

    public ISerializationSurrogate GetSurrogate(System.Type type, StreamingContext context, out ISurrogateSelector selector) 
    { 
     selector = null; 
     return _surrogate; 
    } 
} 


public class BackingFieldSerializationSurrogate : ISerializationSurrogate 
{ 
    private static Regex _backingFieldRegex = new Regex("<(.*)>k__BackingField", RegexOptions.Singleline | RegexOptions.Compiled); 

    public void GetObjectData(object obj, SerializationInfo info, StreamingContext context) 
    { 
     info.SetType(obj.GetType()); 
     var fields = obj.GetType().GetFields(BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); 
     foreach (var field in fields) 
     { 
      string propertyName; 
      info.AddValue(
       TryGetPropertyNameFromBackingField(field.Name, out propertyName) ? GenerateCustomBackingFieldName(propertyName) : 
       field.Name, 
       field.GetValue(obj), 
       field.FieldType); 
     } 
    } 

    public object SetObjectData(object obj, SerializationInfo info, StreamingContext context, ISurrogateSelector selector) 
    { 
     var fields = obj.GetType().GetFields(BindingFlags.FlattenHierarchy | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); 

     foreach (var field in fields) 
     { 
      string propertyName; 
      field.SetValue(
       obj, 
       info.GetValue(
       TryGetPropertyNameFromBackingField(field.Name, out propertyName) ? GenerateCustomBackingFieldName(propertyName) : 
       field.Name, 
       field.FieldType)); 
     } 

     return obj; 
    } 

    private static bool TryGetPropertyNameFromBackingField(string fieldName, out string propertyName) 
    { 
     var match = _backingFieldRegex.Match(fieldName); 
     if (match.Groups.Count == 1) 
     { 
      propertyName = null; 
      return false; 
     } 

     propertyName = match.Groups[1].Value; 
     return true; 
    } 

    private static string GenerateCustomBackingFieldName(string propertyName) 
    { 
     return "_" + propertyName + "_k__BackingField"; 
    } 
} 

[Serializable] 
public class Member 
{ 
    public int Age { get; set; } 

    public string Name { get; set; } 

    public override string ToString() 
    { 
     return string.Format("Member {0}, Age: {1}", Name, Age); 
    } 
} 

[Serializable] 
public class Club 
{ 
    public string Name { get; set; } 

    public IList<Member> Members { get; private set; } 

    public Club(string name, IEnumerable<Member> members) 
    { 
     Name = name; 
     //Members = new List<Member>(members); 
     Members = members.ToArray(); 
    } 

    public override string ToString() 
    { 
     if (Members == null) 
     { 
      return Name; 
     } 

     return Name + ", Total members: " + Members.Count + "\n\t" + string.Join("\n\t", Members); 
    } 
} 

如果有人奇怪的是,我想這種技術的原因是爲了避開這會導致內部處理XmlException其中有我關心的性能問題的問題。

System.Xml.XmlException occurred 
    HResult=-2146232000 
    Message=Name cannot begin with the '<' character, hexadecimal value 0x3C. 
    Source=System.Xml 
    LineNumber=0 
    LinePosition=1 
    StackTrace: 
     at System.Xml.XmlConvert.VerifyNCName(String name, ExceptionType exceptionType) 
    InnerException: 

回答

0

對不起,我沒有給別人任何迴應的機會,但我發現什麼似乎是潛在的問題。在查看SurrogateSelector類的基礎之後,我意識到也許我沒有爲集合返回正確的代理類型,並導致我失去了幾條路徑,直到我最終試圖在這些情況下嘗試檢測數組並從GetSurrogate返回null。就像魔術一樣,一切似乎都按預期開始工作。

public ISerializationSurrogate GetSurrogate(Type type, StreamingContext context, out ISurrogateSelector selector) 
{ 
    if (type.IsArray) 
    { 
     selector = null; 
     return null; 
    } 

    selector = null; 
    return _surrogate; 
} 

起初我有點驚訝,這也似乎糾正其他類型的泛型集合一樣,我也有與問題,但本質上看來,這些集合仍然分成數組和直接對象名單所以也許數組是我唯一缺少的特例。我希望這可以幫助那些遇到類似問題的人處理使用序列化替代類的複雜性。