2013-10-03 87 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; 
} 

起初我有点惊讶,这也似乎纠正其他类型的泛型集合一样,我也有与问题,但本质上看来,这些集合仍然分成数组和直接对象名单所以也许数组是我唯一缺少的特例。我希望这可以帮助那些遇到类似问题的人处理使用序列化替代类的复杂性。