我正在嘗試使用通用序列化代理來控制通過使用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: