NetDataContractSerializer を使用してシリアル化されるフィールドの名前付けを制御するために、一般的なシリアル化サロゲートを使用しようとしています。GetObjectData メソッド内のコレクションへの参照を保持するフィールドに遭遇するたびに問題が発生することを除いて、概念はこれまでのところうまく機能しており、AddValue を呼び出してそれをフィードすると、コレクション型がアイテムにならないようです。連載中のコレクション。配列やリストなどのコレクションに対して何か特別なことをする必要がありますか?
誰かが私を正しい方向に向けることができることを期待して、以下の例を含む完全なコードのコピーを追加しました。
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: