我正在尝试使用通用序列化代理来控制通过使用 NetDataContractSerializer 进行序列化的字段的命名。到目前为止,这个概念运行良好,除了每当我遇到一个在 GetObjectData 方法中保存对集合的引用的字段时,我都会遇到一个问题在被序列化的集合中。对于数组或列表等集合,我需要做些什么特别的事情吗?
我添加了我的完整代码的副本以及下面的示例,希望有人能够指出我正确的方向。
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: